Get generic type of class at runtime

Viewed 762163

How can I achieve this?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Everything I have tried so far always returns type Object rather than the specific type used.

30 Answers

Use Guava.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

A while back, I posted some full-fledge examples including abstract classes and subclasses here.

Note: this requires that you instantiate a subclass of GenericClass so it can bind the type parameter correctly. Otherwise it'll just return the type as T.

Here is my solution. The examples should explain it. The only requirement is that a subclass must set the generic type, not an object.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}

If you have a class like:

public class GenericClass<T> {
    private T data;
}

with T variable, then you can print T name:

System.out.println(data.getClass().getSimpleName()); // "String", "Integer", etc.

If you are working with spring:

public static Class<?>[] resolveTypeArguments(Class<?> parentClass, Class<?> subClass) {
    if (subClass.isSynthetic()) {
        return null;
    }
    return GenericTypeResolver.resolveTypeArguments(subClass, parentClass);
}

By the way, GenericTypeResolver will still get null for the non-subclasses class like the question mentioned, because the generic info of such class was completely erased after compilation.

The only way to solve this question may be:

public class GenericClass<T>
{
    private final Class<T> clazz;
    public Foo(Class<T> clazz) {
        this.clazz= clazz;
    }
    
    public Type getMyType()
    {
        return clazz;
    }
}

If you cannot change the generic class and use one of the method already explained on this page, then simple approach would be to get the type class based on the runtime instance class name.

Class getType(GenericType runtimeClassMember){
if (ClassA.class.equals(runtimeClassMember.getClass()){
  return TypeForClassA.class;
} else if (ClassB.class.equals(runtimeClassMember.getClass()){
  return TypeForClassB.class;
} 

//throw an expectation or do whatever you want for the cases not described in the if section.
}

Use an abstract method that returns the class type then use it in that class and wherever you extend generic class you will have to implement that abstract method to return the required class type

public class AbsractService<T>{
  public abstract Class<T> getClassType ();
   .......
}

at runtime

class AnimalService extends AbstractService<MyType>{
    
public Class<MyType> getClassType (){
        return MyType.class;
 }

  .....
}

I did the same as @Moesio Above but in Kotlin it could be done this way:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}

This was inspired by Pablo's and CoolMind's answers. Occasionally I have also used the technique from kayz1's answer (expressed in many other answers as well), and I believe it is a decent and reliable way to do what the OP asked.

I chose to define this as an interface (similar to PJWeisberg) first because I have existing types that would benefit from this functionality, particularly a heterogeneous generic union type:

public interface IGenericType<T>
{
    Class<T> getGenericTypeParameterType();
}

Where my simple implementation in a generic anonymous interface implementation looks like the following:

//Passed into the generic value generator function: toStore
//This value name is a field in the enclosing class.
//IUnionTypeValue<T> is a generic interface that extends IGenericType<T>
value = new IUnionTypeValue<T>() {
    ...
    private T storedValue = toStore;
    ...
    
    @SuppressWarnings("unchecked")
    @Override
    public Class<T> getGenericTypeParameterType()
    {
        return (Class<T>) storedValue.getClass();
    }
}

I imagine this could be also implemented by being built with a class definition object as the source, that's just a separate use-case. I think the key is as many other answers have stated, in one way or another, you need to get the type information at runtime to have it available at runtime; the objects themselves maintain their type, but erasure (also as others have said, with appropriate references) causes any enclosing/container types to lose that type information.

It might be useful to someone. You can Use java.lang.ref.WeakReference; this way:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}
Related