How to determine the class of a generic type?
Still the same problems : Generic informations are erased at runtime, it cannot be recovered. A workaround is to pass the class T in parameter of a static method :
public class MyGenericClass<T> {
private final Class<T> clazz;
public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
return new MyGenericClass<U>(clazz);
}
protected MyGenericClass(Class<T> clazz) {
this.clazz = clazz;
}
public void doSomething() {
T instance = clazz.newInstance();
}
}
It's ugly, but it works.
I was just pointed to this solution:
import java.lang.reflect.ParameterizedType;
public abstract class A<B> {
public Class<B> g() throws Exception {
ParameterizedType superclass =
(ParameterizedType) getClass().getGenericSuperclass();
return (Class<B>) superclass.getActualTypeArguments()[0];
}
}
This works if A
is given a concrete type by a subclass:
new A<String>() {}.g() // this will work
class B extends A<String> {}
new B().g() // this will work
class C<T> extends A<T> {}
new C<String>().g() // this will NOT work
Unfortunately Christoph's solution as written only works in very limited circumstances. [EDIT: as commented below I no longer remember my reasoning for this sentence and it is likely wrong: "Note that this will only work in abstract classes, first of all."] The next difficulty is that g()
only works from DIRECT subclasses of A
. We can fix that, though:
private Class<?> extractClassFromType(Type t) throws ClassCastException {
if (t instanceof Class<?>) {
return (Class<?>)t;
}
return (Class<?>)((ParameterizedType)t).getRawType();
}
public Class<B> g() throws ClassCastException {
Class<?> superClass = getClass(); // initial value
Type superType;
do {
superType = superClass.getGenericSuperclass();
superClass = extractClassFromType(superType);
} while (! (superClass.equals(A.class)));
Type actualArg = ((ParameterizedType)superType).getActualTypeArguments()[0];
return (Class<B>)extractClassFromType(actualArg);
}
This will work in many situations in practice, but not ALL the time. Consider:
public class Foo<U,T extends Collection<?>> extends A<T> {}
(new Foo<String,List<Object>>() {}).g();
This will throw a ClassCastException
, because the type argument here isn't a Class
or a ParameterizedType
at all; it's the TypeVariable
T
. So now you would be stuck trying to figure out what type T
was supposed to stand for, and so on down the rabbit hole.
I think the only reasonable, general answer is something akin to Nicolas's initial answer -- in general, if your class needs to instantiate objects of some other class that is unknown at compile-time, users of your class need to pass that class literal (or, perhaps, a Factory) to your class explicitly and not rely solely on generics.
i find another way to obtain the Class of the generic object
public Class<?> getGenericClass(){
Class<?> result =null;
Type type =this.getClass().getGenericSuperclass();
if(type instanceofParameterizedType){
ParameterizedType pt =(ParameterizedType) type;
Type[] fieldArgTypes = pt.getActualTypeArguments();
result =(Class<?>) fieldArgTypes[0];
}
return result;
}