Reputation: 1107
As you will see below. I am writing a class named Property that can be bound to any Serializable type as evident from the class description.
Now the value in the property is auto bound to be of type T
during compilation.
I want to implement a Class getType()
method that should return the Class
object of the value at runtime i.e.
Property<String> p = new Property<String>();
Class<String> cl = p.getType();
Here I expect cl to be String.class. Of course one way is:
return value == null ? null : value.getClass();
The issue is it won't reflect in the type returned and returns a raw type of Class
object.
Ideally I want it to be of type Class<String>
public class Property<T extends Serializable> implements Serializable {
private T value = null ;
private String name = null ;
private boolean dirty = false ;
private Entity parent = null ;
public Class getType() {
// Here I want to determine the type of T that this object is bound to ?
return class;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isDirty() {
return dirty;
}
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
public Entity getParent() {
return parent;
}
public void setParent(Entity parent) {
this.parent = parent;
}
}
Upvotes: 0
Views: 187
Reputation: 719307
It is not possible the way you are doing it due to type erasure. One consequence is that this the type that is used to instantiate the type parameter cannot be directly determined at runtime.
Here are a couple of alternatives:
1) Use getClass() to get the type of the value of the Property.
public Class getType() {
return value.getClass();
}
2) Explicitly pass the Class
object for the actual type of T as constructor parameter. Note that the generic typing means that you can't accidentally pass the wrong Class
object
private T value = null;
private Class<T> type;
public Property(Class<T> type) { this.type = type; }
public Class<T> getType() { this.type; }
There is another approach (which probably won't work here from a design perspective) where you reify the Property classes; e.g.
public class IntegerProperty extends Property<Integer> {
public Class getType() {
returns Integer.class;
}
}
There are clever variations of this where the subclass of the generic class in an anonymous class, and/or you access the type parameter via getClass().getTypeParameters()
. But note that the getTypeParameters()
approach only works if you've extended a generic class with specific types for the type parameters.
Upvotes: 1
Reputation: 425238
In short, you can't, because at runtime the type has been erased.
But, you can do this (abbreviated to just the relevant code):
public class Property<T extends Serializable> implements Serializable {
private T value = null;
private final Class<T> clazz;
// The constructor requires an instance of the Class<T>
public Property(Class<T> clazz) {
this.clazz = clazz;
}
// Return a typed Class object
public Class<T> getType() {
return clazz; // echo back a typed Class object pass to the constructor
}
The Class<T>
object passed into the constructor is generally called a "type token".
Upvotes: 6
Reputation: 60968
You can write a method
public Class<? extends T> getType() {
return value == null ? null : value.getClass();
}
This will return the runtime type of value
, and at compile-time provide as much information about the returned class as possible: it will be T
or any subclass of T
.
If you want to always return Class<T>
, then you have to provide the class as a runtime argument, the way @Bohemian describes it. The type arguments of a generic class aren't available at runtime due to type erasure.
Upvotes: 1