How to get the generic type at runtime?
This is my code: The ExecutorImp extends AbstractExecutor which extract the same execute logics of its implementers(ExecutorImp is one case),when calling the execute() method of ExecutorImp, it will call the method in its supertype,but the supertype (the AbstractExcutor) should know another class binding to the implementer(in the example, it is the User class):
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
abstract class AbstractExecutor<E> {
public void execute() throws Exception {
ArrayList<E> list = new ArrayList<E>();
// here I want to get the real type of 'E'
Class cl = this.getClass().getTypeParameters()[0].getGenericDeclaration().getClass();
Object o = cl.getConstructor(String.class).newInstance("Gate");
list.add((E) o);
System.out.println(format(list));
}
public abstract String format(ArrayList<E> list);
public abstract String getType();
}
public class ExectorImp<E> extends AbstractExecutor<User> {
@Override
public String getType() {
return "user";
}
@Override
public String format(ArrayList<User> list) {
StringBuffer sb = new StringBuffer();
for (User u : list) {
sb.append(u.toString() + " ");
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
new ExectorImp().execute();
}
}
class User {
String name;
public User(String name) {
this.name = name;
}
}
开发者_开发知识库
SO, what is the problem with my codes?
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.
精彩评论