Get generic type of class at runtime

后端 未结 26 2513
野的像风
野的像风 2020-11-21 04:40

How can I achieve this?

public class GenericClass
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}
相关标签:
26条回答
  • 2020-11-21 05:04

    I found this to be a simple understandable and easily explainable solution

    public class GenericClass<T> {
    
        private Class classForT(T...t) {
            return t.getClass().getComponentType();
        }
    
        public static void main(String[] args) {
            GenericClass<String> g = new GenericClass<String>();
    
            System.out.println(g.classForT());
            System.out.println(String.class);
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:05

    Technique described in this article by Ian Robertson works for me.

    In short quick and dirty example:

     public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
     {
        /**
         * Method returns class implementing EntityInterface which was used in class
         * extending AbstractDAO
         *
         * @return Class<T extends EntityInterface>
         */
        public Class<T> returnedClass()
        {
            return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
        }
    
        /**
         * Get the underlying class for a type, or null if the type is a variable
         * type.
         *
         * @param type the type
         * @return the underlying class
         */
        public static Class<?> getClass(Type type)
        {
            if (type instanceof Class) {
                return (Class) type;
            } else if (type instanceof ParameterizedType) {
                return getClass(((ParameterizedType) type).getRawType());
            } else if (type instanceof GenericArrayType) {
                Type componentType = ((GenericArrayType) type).getGenericComponentType();
                Class<?> componentClass = getClass(componentType);
                if (componentClass != null) {
                    return Array.newInstance(componentClass, 0).getClass();
                } else {
                    return null;
                }
            } else {
                return null;
            }
        }
    
        /**
         * Get the actual type arguments a child class has used to extend a generic
         * base class.
         *
         * @param baseClass the base class
         * @param childClass the child class
         * @return a list of the raw classes for the actual type arguments.
         */
        public static <T> List<Class<?>> getTypeArguments(
                Class<T> baseClass, Class<? extends T> childClass)
        {
            Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
            Type type = childClass;
            // start walking up the inheritance hierarchy until we hit baseClass
            while (!getClass(type).equals(baseClass)) {
                if (type instanceof Class) {
                    // there is no useful information for us in raw types, so just keep going.
                    type = ((Class) type).getGenericSuperclass();
                } else {
                    ParameterizedType parameterizedType = (ParameterizedType) type;
                    Class<?> rawType = (Class) parameterizedType.getRawType();
    
                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                    TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                    for (int i = 0; i < actualTypeArguments.length; i++) {
                        resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                    }
    
                    if (!rawType.equals(baseClass)) {
                        type = rawType.getGenericSuperclass();
                    }
                }
            }
    
            // finally, for each actual type argument provided to baseClass, determine (if possible)
            // the raw class for that type argument.
            Type[] actualTypeArguments;
            if (type instanceof Class) {
                actualTypeArguments = ((Class) type).getTypeParameters();
            } else {
                actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
            }
            List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
            // resolve types by chasing down type variables.
            for (Type baseType : actualTypeArguments) {
                while (resolvedTypes.containsKey(baseType)) {
                    baseType = resolvedTypes.get(baseType);
                }
                typeArgumentsAsClasses.add(getClass(baseType));
            }
            return typeArgumentsAsClasses;
        }
      }
    
    0 讨论(0)
  • 2020-11-21 05:07

    I have seen something like this

    private Class<T> persistentClass;
    
    public Constructor() {
        this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];
     }
    

    in the hibernate GenericDataAccessObjects Example

    0 讨论(0)
  • 2020-11-21 05:07

    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;
        }
    
    }
    
    0 讨论(0)
  • 2020-11-21 05:08

    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>
        }
    
    }
    
    0 讨论(0)
  • 2020-11-21 05:09

    Sure, you can.

    Java does not use the information at run time, for backwards compatibility reasons. But the information is actually present as metadata and can be accessed via reflection (but it is still not used for type-checking).

    From the official API:

    http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

    However, for your scenario I would not use reflection. I'm personally more inclined to use that for framework code. In your case I would just add the type as a constructor param.

    0 讨论(0)
提交回复
热议问题