Actually, this is not as easy as it seems. There is a problem when you have rich type hierarchy and want to get generic parameter in the supertype. For example, you may have the following hierarchy:
public abstract class BaseDao<T extends BaseEntity> {
...
}
public abstract class SpecialDao<X extends SomeType, E extends BaseEntity> extends BaseDao<E> {
...
}
public class MyDao extends SpecialDao<TypeImpl, EntityImpl> {
...
}
Calling getClass().getGenericSuperclass()
in an instance of MyDao
returns SpecialDao<TypeImpl, EntityImpl>
, but when you call it inside BaseDao
method, you don't know how deep the generic hierarchy is. Moreover, as far as I know, you cannot obtain generic supertype of a supertype. Thus, when you invoke getClass().getGenericSuperclass().getRawType().getGenericSuperclass()
(with some typecasting omitted for readability), you'll get BaseDao<E>
(notice <E>
instead of <T>
). Since getRawType()
strips all type-variable mapping from the type, we're starting with unmapped type variables X
and E
. Then getGenericSuperclass()
just maps these type variables to their positions in BaseDao
.
This behavior can be used so that we keep mapping from type variables to their actual values while traversing the type hierarchy. When we hit the class we want, we simply look up its type parameters in the map. Here is the code:
@SuppressWarnings("unchecked")
public static <T> Class<T> getGenericClassParameter(final Class<?> parameterizedSubClass, final Class<?> genericSuperClass, final int pos) {
// a mapping from type variables to actual values (classes)
Map<TypeVariable<?>, Class<?>> mapping = new HashMap<>();
Class<?> klass = parameterizedSubClass;
while (klass != null) {
Type type = klass.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parType = (ParameterizedType) type;
Type rawType = parType.getRawType();
if (rawType == genericSuperClass) {
// found
Type t = parType.getActualTypeArguments()[pos];
if (t instanceof Class<?>) {
return (Class<T>) t;
} else {
return (Class<T>) mapping.get((TypeVariable<?>)t);
}
}
// resolve
Type[] vars = ((GenericDeclaration)(parType.getRawType())).getTypeParameters();
Type[] args = parType.getActualTypeArguments();
for (int i = 0; i < vars.length; i++) {
if (args[i] instanceof Class<?>) {
mapping.put((TypeVariable)vars[i], (Class<?>)args[i]);
} else {
mapping.put((TypeVariable)vars[i], mapping.get((TypeVariable<?>)(args[i])));
}
}
klass = (Class<?>) rawType;
} else {
klass = klass.getSuperclass();
}
}
throw new IllegalArgumentException("no generic supertype for " + parameterizedSubClass + " of type " + genericSuperClass);
}