How to get the generic type at runtime?
There's some confusion here. Due to type erasure you can't get type information from the runtime parameterized type like:
Class<E> cls = E.getClass(); // Error.
E e = new E(); // Error.
However, you can obtain compiletime parameterized type information from class, field and method declaration by ParameterizedType#getActualTypeArguments()
.
abstract class AbstractExecutor<E> {
public void execute() throws Exception {
List<E> list = new ArrayList<E>();
Class<E> cls = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
E e = cls.getConstructor(String.class).newInstance("Gate");
list.add(e);
System.out.println(format(list));
}
// ...
}
Update: as to whether this is recommended or not, although this will work, this is sensitive to runtime problems whenever minor changes in the class declaration occur. You as developer should document it properly. As a completely different alternative, you can make use of polymorphism.
abstract class AbstractExecutor<E> {
public void execute() throws Exception {
List<E> list = new ArrayList<E>();
E e = create("Gate");
list.add(e);
System.out.println(format(list));
}
public abstract E create(String name);
// ...
}
and implement UserExecutor
accordingly.
class UserExecutor extends AbstractExecutor<User> {
@Override
public User create(String name) {
return new User(name);
}
// ...
}
I think you should use getActualTypeParameters
; as getTypeParameters
does not refer to what has been put in your current instantiation in place of E
, but to E
itself (to describe how is it bounded, etc.).
In order to get the ParameterizedType
you should use getGenericSuperclass
first.
update: but the above only works if the current object is derived from a generic class with the generic argument instantiated, like:
class StringList extends ArrayList<String> {
public Type whatsMyGenericType() {
return ((ParameterizedType)getGenericSuperClass()).getActualTypeParameters()[0];
}
}
should return String.class
.
I don't think you could get the generic type at runtime. The generic type is a restriction that applies at compile time. As I remember at runtime there is no difference between a generic collection and a collection without a generic type.
Usual approach to fix the problem is to slightly change the code. Define constructor on the base class accepting Class<E>
parameter. Assign this parameter to internal field.
On the subclass define constructor without parameters and call super(User.class)
from there.
This way you will know class of argument without much overburden for clients of subclasses.