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);
}
}
Related
Today I've been working with generic/parametric types in java () exercising my polymorphism understanding but since quite some time now I've been having an issue where I would have a generic class with a single method that returns a value, the reason is because I want to be able to call a method and by context been able to have different return types
class dummy <E>{
E value;
public E getValue(){
return value;
}
class dummy1 extends dummy<E>{
int value;
public int getValue(){
return int;
}
class dummy2 extends dummy<E>{
boolean value;
public boolean getValue(){
return boolean;
}
Is this possible? I'm not really sure if even this is the correct way of doing this kind of thing
The problem you have is that in Java primitives, such as int and boolean, cannot be generic arguments.
You need to swap these for equivalent reference types: Integer and Boolean.
Also E is scoped to the dummy class. You will need to provide the type argument to the superclass.
class Dummy<E> {
private E value;
public Dummy(E value) {
this.value = value;
}
public E getValue() {
return value;
}
}
class Dummy1 extends Dummy<Integer> {
public Dummy1(Integer value) {
this.value = value;
}
}
class Dummy2 extends Dummy<E> {
public Dummy2(Boolean value) {
this.value = value;
}
}
Or:
abstract class Dummy<E> {
public abstract E getValue();
}
class ReferenceDummy<E> extends Dummy<E> {
private final E value;
public Dummy1(E value) {
this.value = value;
}
#Override public E getValue() {
return value;
}
}
class Dummy1 extends Dummy<Integer> {
private int value;
public Dummy1(int value) {
this.value = value;
}
#Override public Integer getValue() {
return value;
}
}
class Dummy2 extends Dummy<Boolean> {
private boolean value;
public Dummy1(boolean value) {
this.value = value;
}
#Override public Boolean getValue() {
return value;
}
}
Note that, say, boolean getValue() cannot override Boolean getValue() for some reason.
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
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());
}
}
}
I have the following class:
public final class Param<T>
{
private final String name;
private T value;
public Param(String name)
{
this.name = name;
}
public void setValue(T value) { this.value = value;}
public T getValue() { return value; }
}
I would instantiate it in the following way:
Param<Long> foo = new Param<>("bar");
Param<String> baz = new Param<>("foo");
I want to add a public boolean validate(String value) method to this class, which would do something like the following:
public boolean validate(String value)
{
try
{
if (this.value instanceof String)
return true;
if (this.value instanceof Long)
this.value = (T) Long.valueOf(value);
else if (this.value instanceof Integer)
this.value = (T) Integer.valueOf(value);
//Snip.. a few more such conditions for Double, Float, and Booleans
return true;
}
catch (NumberFormatException e)
{
return false;
}
}
My question is, will the above method work, for correctly determining the 'type' of T and for correctly casting string value to T value? Or should I use a different approach for validating the parameters?
Java generic types are not reified, meaning you need to have some other mechanism for keeping track of them if you need runtime information about them.
A common approach is to pass in a Class<? extends T> token in the constructor:
public final class Param<T>
{
private final String name;
private T value;
private Class<? extends T> type;
public Param(String name, Class<? extends T> type)
{
this.name = name;
this.type = type;
}
public boolean isString() {
return String.class.isAssignableFrom(type);
}
}
So, uh, you want to:
if (this.value instanceof Long)
this.value = (T) Long.valueOf(value);
If value is a Long, convert it to a Long, cast it to a Long, and store it back in value. What?
No. No, I don't think that's going to work.
I have two very simple classes, one extends the other:
public class LocationType implements Parcelable {
protected int locid = -1;
protected String desc = "";
protected String dir = "";
protected double lat = -1000;
protected double lng = -1000;
public LocationType() {}
public int getLocid() {
return locid;
}
public void setLocid(int value) {
this.locid = value;
}
public String getDesc() {
return desc;
}
public void setDesc(String value) {
this.desc = value;
}
public String getDir() {
return dir;
}
public void setDir(String value) {
this.dir = value;
}
public double getLat() {
return lat;
}
public void setLat(double value) {
this.lat = value;
}
public double getLng() {
return lng;
}
public void setLng(double value) {
this.lng = value;
}
// **********************************************
// for implementing Parcelable
// **********************************************
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt (locid);
dest.writeString(desc );
dest.writeString(dir );
dest.writeDouble(lat );
dest.writeDouble(lng );
}
public static final Parcelable.Creator<LocationType> CREATOR = new Parcelable.Creator<LocationType>() {
public LocationType createFromParcel(Parcel in) {
return new LocationType(in);
}
public LocationType[] newArray(int size) {
return new LocationType[size];
}
};
private LocationType(Parcel dest) {
locid = dest.readInt ();
desc = dest.readString();
dir = dest.readString();
lat = dest.readDouble();
lng = dest.readDouble();
}
}
and:
public class MyLocationType extends LocationType {
private ArrayList<ArrivalType> mArrivals = new ArrayList<ArrivalType>();
public List<ArrivalType> getArrivals() {
return mArrivals;
}
public void addArrival(ArrivalType arrival) {
mArrivals.add(arrival);
}
}
The problem is that when I cast an instance of LocationType to MyLocationType I get a ClassCastException. Why is this?
Because LocationType is the superclass; it can't be cast to the subclass.
To explain a bit further: you can only cast up the inheritance tree, which is to say, an object can only be cast as the class type it was created as, any of its superclasses, or any interface that it implements. Thus, a String can be cast as either a String or an Object; a HashMap can be cast as a HashMap, an AbstractMap Map, or an Object.
In your case, a MyLocationType can be either a MyLocationType or a LocationType (or an Object), but not the other way around.
The Java docs on inheritance are pretty good, just to review here.
This is because LocationType is not an instance of MyLocationType it is it's parent. For example if you added new methods to MyLocationType not in LocationType and then cast LocationType to MyLocationType what would expect to happen if these new methods were called that do not exist in the base LocationType?
MyLocationType is a LocationType but LocationType is not a MyLocationType.
Because MyLocationType is of type LocationType and can be cast to one, but LocationType is not of MyLocationType and therefor cannot be cast to it.
LocationType lt = new LocationType();
MyLocationType myLt = new MyLocationType();
LocationType t1 = (LocationType)lt; // OK, but cast not required
LocationType t2 = (LocationType)myLt; // OK, but cast ALSO not required
MyLocationType t3 = (MyLocationType)lt;
// ClassCastException, MyLocationType extends (is-a) LocationType,
// not the other way around
MyLocationType t3 = (MyLocationType)lt;
MyLocationType t5 = (MyLocationType)myLt; // OK, but cast not nessecary
explicit casting down the tree is almost never required, and up the tree is risky:
public void doThis(LocationType lt) {
MyLocationType myLt = (MyLocationType)lt; // DANGEROUS
}
this kind of stuff should only be done if you override a method with a certain
signature, and even then you should check:
#Override
public void doThis(LocationType lt) {
if (lt instanceof MyLocationType) {
MyLocationType myLt = (MyLocationType)lt;
} else {
// A LocationType but not MyLocationType
doSomethingElse(lt);
}
}
Because the reference you're trying to case is not an instance of MyLocationType
This sample always help me to think what's happening.
Object o = new Object();
String s = ( String ) o;
Although String inherits from Object that doesn't mean that every Object is an string ( or in this case, that the "casted" object is an String )