16

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?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
t777
  • 3,099
  • 8
  • 35
  • 53
  • possible duplicate of [Get generic type of java.util.List](http://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list) – Martijn Courteaux Sep 09 '13 at 21:52
  • Can you give a little more context? Why do you need the class of `T`? – arshajii Sep 09 '13 at 21:53
  • 2
    @MartijnCourteaux I have seen these question before I have asked my question but I did not found an answer there. So I do not think of an duplicate. – t777 Sep 09 '13 at 21:55
  • @arshajii It is a Spring/Hibernate-DAO (aka Repository) and Spring needs the class of the entity. (I agree with you, that there are better approaches, but I am bound to the given API.) – t777 Sep 09 '13 at 21:56
  • @arshajii Thanks you. That is already my current workaround. I thought, there is a better way ... :( – t777 Sep 09 '13 at 21:58
  • This is a kickoff sample http://ideone.com/676Wyw see my answers for details. – Arturo Volpe Sep 10 '13 at 11:24
  • More accurately: possible duplicate of [how to get class instance of generics type T](http://stackoverflow.com/questions/3437897/how-to-get-class-instance-of-generics-type-t) – Paul Bellora Sep 10 '13 at 14:37
  • @t777 BalusC's answer explains that in your particular case it is possible to ascertain the class of `T`. I urge you to accept his answer instead. – arshajii Sep 10 '13 at 22:21

7 Answers7

27

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

    // ...
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
13

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);
}
Marcel Šebek
  • 291
  • 1
  • 4
  • 8
11

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

}
eugene82
  • 8,432
  • 2
  • 22
  • 30
9

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

Arturo Volpe
  • 3,442
  • 3
  • 25
  • 40
  • This worked on one of two machines, with the second machine complaining that `sun.reflect.generics.reflectiveObjects.TypeVariableImpl` cannot cast to `Class`. Is there a way to make this more reliable? [See here](https://stackoverflow.com/a/48604701/6112457) for the context I'm using it in. – Zoey Hewll Feb 05 '18 at 07:32
  • The error you see is because the runtime proxy your supper class, and you don't really get the super class but the proxy class, you can check this by adding a `System.out.println(type.getClass().getName())`, and verify if the class is the correct, if not, do something like `if (type.getClass().getName().startsWith('$')) type .= type.getGenericSuperclass();` – Arturo Volpe Feb 05 '18 at 19:28
1

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

  // ...

}
Farid
  • 2,265
  • 18
  • 14
1

You might check out TypeTools (which I authored) for this:

Class<T> t = (Class<T>)TypeResolver.resolveRawArgument(BaseDao.class, getClass());
Jonathan
  • 5,027
  • 39
  • 48
1

You can pass the type of class through the constructor of the abstract class, like this.

public abstract class BaseDao<T extends PersistentObject> {

    private final Class<T> typeClass;

    //Here in the constructor, you are asking for the actual Type Class.
    BaseDao(Class<T> typeClass) {
        this.typeClass = typeClass;
    }

   // ...

}

And in the concrete class pass the class type in the constructor.

public class BaseDaoImpl extends BaseDao<ActualType> {
   
   BaseDaoImpl() {
       //Here in the implementation, you pass the type class
       super(ActualType.class);
   }
}

The problem with this approach is that you cannot ensure that the type you are passing through the constructor is the actual type is being set in generic, so this can lead to issues if developers do not make sure they are passing the correct type through the constructor.

Felipe Bejarano
  • 516
  • 5
  • 8