I have a class like:
public abstract class BaseDao<T extends PersistentObject> {
protected Class<T> getClazz() {
return T.class;
}
// ...
}
But the compiler says to T.class;
: Illegal class literal for the type parameter T
.
How can I get the class of T
?
You can get around the superfluous reference by providing a generic static factory method. Something like public static <T> GenericClass<T> of(Class<T> type) {...} and then call it as such: GenericClass<String> var = GenericClass. of(String. class) .
The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. A solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.
The actual type arguments of a generic type are. reference types, wildcards, or. parameterized types (i.e. instantiations of other generic types).
A class interface or a method that operates on a parameterized type is called generic, like generic class or generic method, and generics only works with objects. And their type differs based on their type arguments.
It's definitely possible to extract it from Class#getGenericSuperclass()
because it's not defined during runtime, but during compiletime by FooDao extends BaseDao<Foo>
.
Here's a kickoff example how you could extract the desired generic super type in the constructor of the abstract class, taking a hierarchy of subclasses into account (along with a real world use case of applying it on generic EntityManager
methods without the need to explicitly supply the type):
public abstract class BaseDao<E extends BaseEntity> {
@PersistenceContext
private EntityManager em;
private Class<E> type;
@SuppressWarnings("unchecked") // For the cast on Class<E>.
public BaseDao() {
Type type = getClass().getGenericSuperclass();
while (!(type instanceof ParameterizedType) || ((ParameterizedType) type).getRawType() != BaseDao.class) {
if (type instanceof ParameterizedType) {
type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
} else {
type = ((Class<?>) type).getGenericSuperclass();
}
}
this.type = (Class<E>) ((ParameterizedType) type).getActualTypeArguments()[0];
}
public E find(Long id) {
return em.find(type, id);
}
public List<E> list() {
return em.createQuery(String.format("SELECT e FROM %s e ORDER BY id", type.getSimpleName()), type).getResultList();
}
// ...
}
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With