About Java nested Enum type - java

I will give the example java code for this circumstances:
// StateEntity interface define:
public interface StateEntity extends IDEntity { // the IDEntity define getId & setId
static int processBitState(int state, boolean op, int pos) {
if (op) {
state = Bits.set(state, pos);
} else {
state = Bits.clear(state, pos);
}
return state;
}
static <E extends Enum<E>, T extends StateEntity> int gatherState(Class<E> enumType, int state, #NotNull T data) {
try {
Method getJsonFieldName = enumType.getMethod("getJsonFieldName");
Method getPosition = enumType.getMethod("getPosition");
BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (E bitSet : EnumSet.allOf(enumType)) {
String fieldName = (String) getJsonFieldName.invoke(bitSet);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
if (propertyDescriptor.getName().equals(fieldName)) {
Method reader = propertyDescriptor.getReadMethod();
Boolean value = (Boolean) reader.invoke(data);
state = processBitState(state, value, (Integer) getPosition.invoke(bitSet));
break;
}
}
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | IntrospectionException e) {
e.printStackTrace();
}
return state;
}
<E extends Enum<E>> Class<E> getStateType();
Integer getState();
void setState(Integer state);
}
// Impl class:
public class StateEntityClass implements StateEntity {
Long id;
Integer state;
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
#Override
public <E extends Enum<E>> Class<E> getStateType() {
return (Class<E>) (BitSet.class);
}
#Override
public Integer getState() {
return state;
}
#Override
public void setState(Integer state) {
this.state = state;
}
public enum BitSet {
ACTIVATION(0),
FREEZE(1),
UPDATE(2);
private final int position;
BitSet(int v) {
position = v;
}
#NotNull
public String getName() {
return name().toLowerCase().replaceAll("_", "-");
}
#NotNull
public String getJsonFieldName() {
return NameConverter.snakeCaseToCamelCase("is_" + name().toLowerCase());
}
public int getPosition() {
return position;
}
}
}
// used:
public class Main {
public static void main(String[] args) {
StateEntityClass e = new StateEntityClass();
// first method, this statement error:
e.setState(StateEntity.gatherState(e.getStateType(), e.getState() == null ? 0 : e.getState(), e));
// second method, but this statement ok:
e.setState(StateEntity.gatherState(StateEntityClass.BitSet.class, e.getState() == null ? 0 : e.getState(), e));
// why?? how change the first method
}
}
I have some class implements StateEntity, so I must use the first method for generic.
How do I change my code to use the first method?
What is the Java way to process these circumstances problems?
The Enum content is unrelated, the StateEntity implements class has self Enum for the state.

The problem I hope to solve is how to deal with an Enum uniformly in Java. This Enum has a unified structure and a unified interface, but the Enumerator contained is different. See the above question for the specific situation. After #tgdavies' prompt, I moved the type parameter from the method definition to the interface definition, which solved the problem. Later, under the reminder of #Guillaume F., I further optimized the use of Enum. Thank you all for participating

Related

get the return value of the method which is from Generic class in java

I created a parent class Repo which has methods for insert, delete, display and delete objects in a list. Repo is a generic class. I created a child classes for Repo (like DepartmentRepo class)and pass Department, Employee, etc.. classes. I want perform insert, delete, display and delete operations on any class objects that passed to Repo class.
I need to get the return value of the method "get" which is from Generic class in java. I can only get the method name from Generic here I mention the code files
public class Department {
private long Id;
private String Name;
private String Location;
public Department() {
}
public Department(long id, String name, String location) {
super();
Id = id;
Name = name;
Location = location;
}
public long getId() {
return Id;
}
public void setId(long id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getLocation() {
return Location;
}
public void setLocation(String location) {
Location = location;
}
}
import java.util.ArrayList;
import java.util.List;
public class Repo<T, U> {
List<T> list = new ArrayList<T>();
public List<T> getAll() {
return list;
}
public void insert(T obj) {
list.add(obj);
}
public T get(U id) throws NoSuchMethodException, SecurityException {
for (T ele : list) {
if (ele.getClass().getMethod("getId") == id) {
return ele;
}
}
return null;
}
public void delete(U id) throws NoSuchMethodException, SecurityException {
list.remove(get(id));
}
}
public class DepartmentRepo extends Repo<Department, Long>{
}
class MainApi
{
public static void main (String[] args)
{
DepartmentRepo dept = new DepartmentRepo ();
Department ict=new Department(10001,"Dept of ICT","Town");
Department cs=new Department(10002,"Dept of Computer Science","Pampaimadu");
Department bio=new Department(10003,"Dept of Bio Science","Pampaimadu");
Department sats=new Department(10004,"Dept of Statistics","Kurumankadu");
dept.insert(ict);
dept.insert(cs);
dept.insert(bio);
dept.insert(sats);
System.out.println();
dept.getAll();
try{
dept.get(10001);
}
catch(Exception e){
}
}
}
As another solution, and an answer to your comment, you could use elements inheritance, and avoid reflection calls and exceptions.
1- Create an element interface OR class
public interface RepoElement<U> {
U getId();
}
OR
public class RepoElement<U> {
private U Id;
public RepoElement() {}
public RepoElement(U id) {
Id = id;
}
public U getId() {
return Id;
}
public void setId(U id) {
Id = id;
}
}
2- Make Department inherit from the interface OR class
public class Department implements RepoElement<Long> {
(...)
public Long getId() {
return Id;
}
(...)
}
OR
public class Department extends RepoElement<Long> {
private String Name;
private String Location;
public Department() {
super();
}
public Department(long id, String name, String location) {
super(id);
Name = name;
Location = location;
}
}
3- Use it directly in the Repo class (and remove exceptions)
public class Repo<T extends RepoElement<U>, U> {
(...)
public T get(U id) {
for (T ele : list) {
if (ele.getId().equals(id)) {
return ele;
}
}
return null;
}
public void delete(U id) {
list.remove(get(id));
}
(...)
}
As a last suggestion, you could use a Map instead of a List in the Repo class, and get rid of any search complexity/optimizations:
public class Repo<T extends RepoElement<U>, U> {
Map<U, T> map = new HashMap<U, T>();
public Collection<T> getAll() {
return map.values();
}
public void insert(T obj) {
map.put(obj.getId(), obj);
}
public T get(U id) {
return map.get(id);
}
public void delete(U id) {
map.remove(id);
}
}
You need to invoke the getId() method so that it will return the id to perform comparison correctly:
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
public class Repo<T, U> {
List<T> list = new ArrayList<T>();
public List<T> getAll() {
return list;
}
public void insert(T obj) {
list.add(obj);
}
public T get(U id) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (T ele : list) {
if (ele.getClass().getMethod("getId").invoke(ele).equals(id)) {
return ele;
}
}
return null;
}
public void delete(U id) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
list.remove(get(id));
}
}

How to Dynamically call and setter and getter methods using Reflection class?

Say I have class AccountPojo and GetAccountPojo with its setter and getter methods as below.
public class AccountPojo {
private String dataList;
private String dataSet;
public String getDataList() {
return dataList;
}
public void setDataList(String dataList) {
this.dataList = dataList;
}
public String getDataSet() {
return dataSet;
}
public void setDataSet(String dataSet) {
this.dataSet = dataSet;
}
}
public class GetAccountsPojo {
private String accountId;
private int noOfAccounts;
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public int getNoOfAccounts() {
return noOfAccounts;
}
public void setNoOfAccounts(int noOfAccounts) {
this.noOfAccounts = noOfAccounts;
}
}
Now I have class Test as below
public Class Test {
public static void main(String[] args) {
Class cls = Class.forName("com.org.temp."+ClassName); // ClassName(AccountPojo/GetAccountPojo) here I know already which class is getting called.
Object clsInstance = (Object) cls.newInstance();
System.out.println("The cls is==" + cls+" and classInstance is=="+clsInstance);
// Here I want to access getter and setter methods of AccountPojo and GetAcoountPojo dynamically, no hard coding
}
}
Have you tried getting all the methods of the invoked class and filtering out only the getter methods by name and invoking them?
Method[] methods = cls.getClass().getDeclaredMethods();
for (Method m: methods) {
if(m.getName().startsWith("get")) {
m.invoke(clsInstance);
}
}
This solves our half problem, as getters are invoked without any arguments. But if you need to invoke a setter method you need to specify arguments. Ex, To invoke a setter which accepts string argument method as below:
m.invoke(clsInstance, "some string argument");
One solution to could be make all the setters accept an object type value and typecast them while assigning it to actual class variables.
Now your pojo classes will look as below:
public class AccountPojo {
private String dataList;
private String dataSet;
public String getDataList() {
return dataList;
}
public void setDataList(Object dataList) {
this.dataList = (String) dataList;
}
public String getDataSet() {
return dataSet;
}
public void setDataSet(Object dataSet) {
this.dataSet = (String)dataSet;
}
}
public class GetAccountsPojo {
private String accountId;
private int noOfAccounts;
public String getAccountId() {
return accountId;
}
public void setAccountId(Object accountId) {
this.accountId = (String) accountId;
}
public int getNoOfAccounts() {
return noOfAccounts;
}
public void setNoOfAccounts(Object noOfAccounts) {
this.noOfAccounts = (int) noOfAccounts;
}
}
Add below code to your main method:
for (Method m: methods) {
if(m.getName().startsWith("get")) {
m.invoke(clsInstance);
}
if(m.getName().startsWith("set")) {
m.invoke(clsInstance, "any argument to be passed here");
}
}
Don't use raw class. If you know which class is called already, use typed class.
try {
AccountPojo obj = AccountPojo.class.newInstance();
Method setDataList = AccountPojo.class.getMethod("setDataList");
setDataList.setAccessible(true); // This is important if you want to access protected or private method. For public method you can skip
setDataList.invoke(obj, "123");
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}

Return object from a list as CustomObject not just raw <T> Object

I have a CustomObject declared as raw type of <T>. And when I populate a List<CustomObject> with new instances of it, I can't get them back as a CustomObject, only as an Object.
public class CustomObject<T> {
private String name;
private T value;
// getters and setters
}
But obviously when I use subclass, all is working as expecting;
public class CustomObject {
private class SubCustomObject<T> {
private String name;
private T value;
}
public CustomObject() {
this.customObject = new SubCustomObject();
private SubCustomObject customObject;
// getters and setters
}
Is there a way to make the first example to behave like the second one, and avoid using extra object and so I could do this:
public class CustomObject<T> {
private String name;
private T value;
private boolean isGroup;
// getters and setters
private void setValue(T value) {
if (value instanceof String) {
this.value = value;
this.isGroup = false;
}
if (value instanceof CustomObject) {
if (isGroup()) {
((List<CustomObject>) this.value).add((CustomObject) value);
} else {
this.value = (T) new ArrayList<CustomObject>();
this.isGroup = true;
setValue(value);
}
}
}
}
public void getItemByName(String name) {
// say the list is already populated
for (CustomObject object : listOfCustomObject) {
String nameField = object.getName();
if (name.equals(nameField) {
System.out.println(nameField);
}
}
}
Instead of this one:
public void getItemByName(String name) {
// say the list is already populated
for (Object object : listOfCustomObject) {
String nameField = ((CustomObject)object).getName();
if (name.equals(nameField) {
System.out.println(nameField);
}
}
}
// Apply that behavior to this and avoid to use inner class.
public class MetadataEntry {
public MetadataEntry() {
this.entity = new Entry();
}
private class Entry<T> {
private String name;
private T value;
private boolean isGroup;
private void setValue(T value) {
if (value instanceof String) {
this.value = value;
this.isGroup = false;
}
if (value instanceof MetadataEntry) {
if (isGroup()) {
((List<MetadataEntry>) this.value).add((MetadataEntry) value);
} else {
this.value = (T) new ArrayList<MetadataEntry>();
this.isGroup = true;
setValue(value);
}
}
}
}
private Entry entity;
public void setName(String name) {
this.entity.name = name;
}
public String getName() {
return this.entity.name;
}
public void setValue(String value) {
entity.setValue(value);
}
public void setValue(MetadataEntry value) {
entity.setValue(value);
}
public boolean isGroup() {
return this.entity.isGroup;
}
public List<MetadataEntity> getChildNodes() {
if (isGroup()) {
return (List<MetadataEntry>) this.entity.value;
}
return null;
}
public String getValue() {
if (!isGroup()) {
return (String) this.entity.value;
}
return null;
}
}
You can not make a list of different types X,Y,Z and put it in a single container of type W. You need to define a bounding parameter on your raw type so that your items and list are of same type. probably your T should be bounded by some interface type or it should extends some class.
Here’s my suggestion. I have abandoned the generics. Instead of just one inner class there is now an abstract inner class with two subclasses, one for groups and one for entries that are not groups. The good news: no cast is necessary anywhere.
public class MetadataEntry {
private String name;
static abstract class Entry {
abstract Entry setValue(String value);
abstract Entry setValue(MetadataEntry value);
abstract boolean isGroup();
abstract List<MetadataEntry> getChildNodes();
abstract String getSimpleValue();
}
static class SimpleEntry extends Entry {
private String value;
public SimpleEntry(String value) {
this.value = value;
}
#Override
Entry setValue(String value) {
this.value = value;
return this;
}
#Override
Entry setValue(MetadataEntry value) {
return new GroupEntry(value);
}
#Override
public boolean isGroup() {
return false;
}
#Override
public List<MetadataEntry> getChildNodes() {
return null;
}
#Override
public String getSimpleValue() {
return value;
}
}
static class GroupEntry extends Entry {
List<MetadataEntry> value;
public GroupEntry(MetadataEntry value) {
this.value = new ArrayList<>();
this.value.add(value);
}
#Override
Entry setValue(String value) {
return new SimpleEntry(value);
}
#Override
Entry setValue(MetadataEntry value) {
this.value.add(value);
return this;
}
#Override
public boolean isGroup() {
return true;
}
#Override
public List<MetadataEntry> getChildNodes() {
return value;
}
#Override
public String getSimpleValue() {
return null;
}
}
private Entry entity;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setValue(String value) {
entity = entity.setValue(value);
}
public void setValue(MetadataEntry value) {
entity = entity.setValue(value);
}
public boolean isGroup() {
return this.entity.isGroup();
}
public List<MetadataEntry> getChildNodes() {
return entity.getChildNodes();
}
public String getValue() {
return entity.getSimpleValue();
}
}
I have used an idea similar to what m 1987 said about a class that returns an instance of itself. I applied it to the inner classes only to free the users of the outer class from caring about this trickery. If you prefer, I am sure it could be applied to the outer class instead. Then you would have an abstrat class on the outer level with two subclasses, and would no longer need the inner classes. This is one of the things you asked for, so you may prefer it, but it comes at a cost: anyone calling setValue() on the outer class would need to remember that they got a new instance back.
I have a CustomObject declared as raw type of <T>.
That doesn't makes sense. You either have a raw type or a generic, not a raw type of a generic.
And when I populate a List with new instances of it, I can't get them back as a CustomObject, only as an Object.
Because your list is not generic (always bad). When you declare a List<Something> it will return Something on a get call. That Something can be generic or a raw type. A List<CustomObject<String>> will not accept a CustomObject<Integer> and using the raw type List<CustomObject> will end in disaster, hence the danger in raw types.
Now let's look at your code. The class
public class CustomObject<T> {
private String name;
private T value;
}
defines an object that behaves the same for any type. In essence what you have here is just a glorified Object with a String serving as its name attached to it.
However, now you do
private void setValue(T value) {
if (value instanceof String)
// ...
if (value instanceof CustomObject)
// ...
}
which separates the behavior for different types. and what happens if the generic type is not a String or a CustomObject?
Let's try to solve your problem. Since generics are meant to unify the behavior, let's look at what the unified behavior is that you're trying to get:
public void getItemByName(String name) {
for (CustomObject object : listOfCustomObject) {
String nameField = object.getName();
// ...
}
}
}
Basically, you require that all the items in listOfCustomObject implement a String getName() method. That's it as far as I can see from your question. That means that your CustomObject<T> should either implement an interface or extend a class (call it Superthing) with that method. Then you will just declare your list as List<? extends Superthing>.
As for the CustomObject itself, it doesn't need to be generic as you hint that there are only 2 types of generics you want to deal with (you have 2 ifs, but no else to deal with a general case). It looks like what you want are 2 different classes with different behaviors that both implement or extend a common supertype.
Try something like this:
abstract class AbstractEntry {
private String name;
protected boolean isGroup;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isGroup() {
return isGroup;
}
}
class MetaEntry extends AbstractEntry {
AbstractEntry value;
MetaEntry(AbstractEntry value) {
this.value = value;
// handle isGroup
}
public void setValue(AbstractEntry value) {
this.value = value;
}
public AbstractEntry getValue() {
if (!isGroup)
return value;
return null;
}
}
class StringEntry extends AbstractEntry {
String value;
StringEntry(String value) {
this.value = value;
isGroup = false;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
I think there is no need of list as it always hold only one element. As #Ole V.V mentioned, the requirement naturally calls for the use of composition and in fact, generic does not fit into your requirements. Here is how I would tackle your requirements:
public interface Named {
public String getName();
public String getValue();
}
public class CustomObject implements Named {
private String name;
private String value;
private boolean isGroup;
// getters and setters
private boolean isGroup() {
return isGroup;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public class CustomObject2 implements Named {
private String name;
private CustomObject value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value.getValue();
}
public void setValue(CustomObject value) {
this.value = value;
}
}
public class DriverCustomObject {
public static void main(String arg[]) {
CustomObject t = new CustomObject();
t.setName("key1");
t.setValue("value1");
CustomObject2 t2 = new CustomObject2();
t2.setName("complex");
t2.setValue(t);
List<Named> list = new ArrayList<Named>();
list.add(t);
list.add(t2);
for (Named l : list) {
System.out.println(l.getName());
System.out.println(l.getValue());
}
}
}

can't deserialize guava type with GWTP-Rest + jackson-guava from Spring service using guava-datatype

I use GWTP-Rest and i need to deserialize an dto which contains an ListMultimap. All the elements of the dto are deserialize but not the ListMultimap. below the code of dto. JsonAnnotation are working and come from the correct artifactId which is compatible with gwt-jackson. I imports with maven gwt-guava-jackson and inherits the module.
public class GetdtoMobileResult implements ExpirableResult, DtoData {
#JsonProperty("diffusions")
private ListMultimap<Integer, Diffusion> diffusions;
private TimeInterval visibleRange;
#JsonProperty("ttl")
private long ttl;
private dtoHourRange hourRange;
GetTVGuideMobileResult() {
super();
}
public GetTVGuideMobileResult(
final ArrayListMultimap<Integer, Diffusion> diffusions,
final TimeInterval visibleRange, final long ttl, final MediamatHourRange range) {
super();
this.diffusions = diffusions;
this.visibleRange = visibleRange;
this.ttl = ttl;
this.hourRange = range;
}
#JsonIgnore
#Override
public ListMultimap<ChannelId, Diffusion> getDiffusions() {
return ArrayListMultimap.create();
}
#JsonProperty("diffusions")
public ListMultimap<Integer, Diffusion> getdiffusions()
{
return diffusions;
}
#Override
public TimeInterval getVisibleRange() {
return visibleRange;
}
#JsonProperty("ttl")
#Override
public long ttl() {
return ttl;
}
#Override
public dtoHourRange getHourRange() {
return hourRange;
}
// #### setter add to able deserialization on client side mobile. ######
#JsonProperty("diffusions")
public void setDiffusions(final ListMultimap<Integer, Diffusion> diffusions) {
this.diffusions = diffusions;
}
public void setHourRange(final dtoHourRange hourRange) {
this.hourRange = hourRange;
}
#JsonProperty("ttl")
public void setTtl(final long ttl) {
this.ttl = ttl;
}
public void setVisibleRange(final TimeInterval visibleRange) {
this.visibleRange = visibleRange;
}
}
I receive the correct object from server {"diffusions":{"..."} ,...}. Serialization is doing by the lib jackson-datatype-guava, which if i correctly understood is using by jackson-guava. Interface which are implements define getter of the object, and implements Serializable.
I have another problem's, probably linked to his, my ListMultimap use normaly an dto as key which wrap an integer, but jackson tell me that my dto is not supported as map's key. Code of my dto :
public class DtoId implements Serializable {
/**
* serial version Uid
*/
private static final long serialVersionUID = -5816632906385308130L;
private int id;
DtoId() {
// for serialization
}
public DtoId(final int id) {
super();
this.id = id;
}
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
#Override
public String toString() {
return Integer.toString(id);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ChannelId other = (ChannelId) obj;
if (id != other.id) {
return false;
}
return true;
}
}
Ok, problem's come from bad dependency between project on guava. I provided same version of guava to the client, shared & server, now serialization works, sorry to the bad question.
Second error continue to occurs, i will post this on an other page.

Subclassing in self-bounding generics

I'm having an issue with a self-bounding generic type which has a self-bounding generic subtype.
I'm trying to implement some kind of builder pattern and i'd like to have my statements more or less like in the main method.
Can anyone help me out in finding a better why to declare the generics so I no longer need the cast and I don't get compilation errors in the statements. Or can anyone explain in clear text why this can't work?
import java.util.Date;
public class SelfBoundingGenericTypeTest {
public static void main(String[] args) {
ConcreteType type = new ConcreteType().pageSize(1).id(12);
SubType type2 = (SubType) new SubType().id(10).pageSize(0); // Why do i need the cast?
SubType type3 = new SubType().pageSize(0).id(10); // Compile error
}
}
abstract class SuperType<E extends SuperType<E>> {
private int _pageSize = Integer.MIN_VALUE;
private int _startIndex = Integer.MIN_VALUE;
#SuppressWarnings("unchecked")
public E pageSize(int value) {
this._pageSize = value;
return (E) this;
}
#SuppressWarnings("unchecked")
public E startIndex(int value) {
this._startIndex = value;
return (E) this;
}
public int getPageSize() {
return _pageSize;
}
public int getStartIndex() {
return _startIndex;
}
}
class SubType<E extends SubType<E>> extends SuperType<E> {
private long _id = Long.MIN_VALUE;
#SuppressWarnings("unchecked")
public E id(long value) {
this._id = value;
return (E) this;
}
public long getId() {
return _id;
}
}
class ConcreteType extends SubType<ConcreteType> {
private Date _startDate;
public Date getStartDate() {
return _startDate;
}
public ConcreteType startDate(Date value) {
this._startDate = value;
return this;
}
}
You need the cast because SubType is a raw type. As such all its members are raw, including those inherited from SuperType. The raw signature of SuperType.pageSize is its erasure SuperType pageSize(int). So the "fix" is to not use raw types. This will all magically work for ConcreteType.
Edit: Don't use raw types. Ever. You should use your ConcreteType, but before you use the stupid, moronic, idiotic "solution" of redeclaring every method, use ((SubType<?>) new SubType()) instead.
I am not really sure about the failing reason but that's my understanding:
abstract class SuperType> {
public E pageSize(int value) { ...}
}
For method pageSize, as you have E declared as extends SuperType<E>, after type-erasure, the method signature is in fact SuperType pageSize(int) , which caused the problem in new SubType().pageSize(0).id(10) because pageSize is returning a SuperType.
Though it doesn't looks as magical as you expect, with covariant return type you can simply "overload" those methods in inherited class:
import java.util.Date;
public class SelfBoundingGenericTypeTest {
public static void main(String[] args) {
ConcreteType type = new ConcreteType().pageSize(1).id(12);
SubType type2 = new SubType().id(10).pageSize(0); // works fine now
SubType type3 = new SubType().pageSize(0).id(10); // works fine too
}
}
abstract class SuperType {
private int _pageSize = Integer.MIN_VALUE;
private int _startIndex = Integer.MIN_VALUE;
public SuperType pageSize(int value) {
this._pageSize = value;
return this;
}
public SuperType startIndex(int value) {
this._startIndex = value;
return this;
}
public int getPageSize() {
return _pageSize;
}
public int getStartIndex() {
return _startIndex;
}
}
class SubType extends SuperType {
private long _id = Long.MIN_VALUE;
public SubType id(long value) {
this._id = value;
return this;
}
public SubType pageSize(int value) {
return (SubType) super.pageSize(value);
}
public SuperType startIndex(int value) {
return (SubType) super.pageSize(value);
}
public long getId() {
return _id;
}
}
class ConcreteType extends SubType {
private Date _startDate;
public Date getStartDate() {
return _startDate;
}
public ConcreteType startDate(Date value) {
this._startDate = value;
return this;
}
public ConcreteType id(long value) {
return (ConcreteType) super.id(value);
}
public ConcreteType pageSize(int value) {
return (ConcreteType) super.pageSize(value);
}
public ConcreteType startIndex(int value) {
return (ConcreteType) super.pageSize(value);
}
}

Categories