Question

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?

Was it helpful?

Solution

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();
    }

    // ...
}

OTHER TIPS

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 Spring framework is available, you can do like here:

import org.springframework.core.GenericTypeResolver;

public abstract class BaseDao<T extends PersistentObject> {

    protected Class<T> getClazz() {
        return (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), BaseDao.class);
    }

}

If your class is abstract, you can try with this:

public class<T> getClassOfT() {
    final ParameterizedType type = (ParameterizedType) this.getClass()
            .getGenericSuperclass();
    Class<T> clazz = (Class<T>) type.getActualTypeArguments()[0];
    return clazz;
}

This only work if the instance is a direct subclass, and the type of the class you want is the first one (see the [0]).

If you have a large hierarchy of dao's, you can try fidn the BaseDao recursively and get the parametrized type

See a example here (see the output in the bottom)

Cheers and sorry for my bad english

Common way to sort this issue in a secure way is to add a constructor to store the class of the type. Example in your context:

public abstract class BaseDao<T extends PersistentObject> {
  private Class<T> classT;

  BaseDao(Class<T> classT){
    this.classT=classT;
  }

  protected Class<T> getClazz() {
     return classT;
  }

  // ...

}

You might check out TypeTools for this:

Class<T> t = (Class<T>)TypeResolver.resolveRawArgument(BaseDao.class, getClass());
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top