How do I get a class instance of generic type T?

前端 未结 22 1246
猫巷女王i
猫巷女王i 2020-11-21 11:03

I have a generics class, Foo. In a method of Foo, I want to get the class instance of type T, but I just can\'t call T.

相关标签:
22条回答
  • 2020-11-21 12:04

    Imagine you have an abstract superclass that is generic:

    public abstract class Foo<? extends T> {}
    

    And then you have a second class that extends Foo with a generic Bar that extends T:

    public class Second extends Foo<Bar> {}
    

    You can get the class Bar.class in the Foo class by selecting the Type (from bert bruynooghe answer) and infering it using Class instance:

    Type mySuperclass = myFoo.getClass().getGenericSuperclass();
    Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
    //Parse it as String
    String className = tType.toString().split(" ")[1];
    Class clazz = Class.forName(className);
    

    You have to note this operation is not ideal, so it is a good idea to cache the computed value to avoid multiple calculations on this. One of the typical uses is in generic DAO implementation.

    The final implementation:

    public abstract class Foo<T> {
    
        private Class<T> inferedClass;
    
        public Class<T> getGenericClass(){
            if(inferedClass == null){
                Type mySuperclass = getClass().getGenericSuperclass();
                Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
                String className = tType.toString().split(" ")[1];
                inferedClass = Class.forName(className);
            }
            return inferedClass;
        }
    }
    

    The value returned is Bar.class when invoked from Foo class in other function or from Bar class.

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

    I found a generic and simple way to do that. In my class I created a method that returns the generic type according to it's position in the class definition. Let's assume a class definition like this:

    public class MyClass<A, B, C> {
    
    }
    

    Now let's create some attributes to persist the types:

    public class MyClass<A, B, C> {
    
        private Class<A> aType;
    
        private Class<B> bType;
    
        private Class<C> cType;
    
    // Getters and setters (not necessary if you are going to use them internally)
    
        } 
    

    Then you can create a generic method that returns the type based on the index of the generic definition:

       /**
         * Returns a {@link Type} object to identify generic types
         * @return type
         */
        private Type getGenericClassType(int index) {
            // To make it use generics without supplying the class type
            Type type = getClass().getGenericSuperclass();
    
            while (!(type instanceof ParameterizedType)) {
                if (type instanceof ParameterizedType) {
                    type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
                } else {
                    type = ((Class<?>) type).getGenericSuperclass();
                }
            }
    
            return ((ParameterizedType) type).getActualTypeArguments()[index];
        }
    

    Finally, in the constructor just call the method and send the index for each type. The complete code should look like:

    public class MyClass<A, B, C> {
    
        private Class<A> aType;
    
        private Class<B> bType;
    
        private Class<C> cType;
    
    
        public MyClass() {
          this.aType = (Class<A>) getGenericClassType(0);
          this.bType = (Class<B>) getGenericClassType(1);
          this.cType = (Class<C>) getGenericClassType(2);
        }
    
       /**
         * Returns a {@link Type} object to identify generic types
         * @return type
         */
        private Type getGenericClassType(int index) {
    
            Type type = getClass().getGenericSuperclass();
    
            while (!(type instanceof ParameterizedType)) {
                if (type instanceof ParameterizedType) {
                    type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
                } else {
                    type = ((Class<?>) type).getGenericSuperclass();
                }
            }
    
            return ((ParameterizedType) type).getActualTypeArguments()[index];
        }
    }
    
    0 讨论(0)
  • 2020-11-21 12:05

    As explained in other answers, to use this ParameterizedType approach, you need to extend the class, but that seems like extra work to make a whole new class that extends it...

    So, making the class abstract it forces you to extend it, thus satisfying the subclassing requirement. (using lombok's @Getter).

    @Getter
    public abstract class ConfigurationDefinition<T> {
    
        private Class<T> type;
        ...
    
        public ConfigurationDefinition(...) {
            this.type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
            ...
        }
    }
    

    Now to extend it without defining a new class. (Note the {} on the end... extended, but don't overwrite anything - unless you want to).

    private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
    private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
    ...
    Class stringType = myConfigA.getType();
    Class fileType = myConfigB.getType();
    
    0 讨论(0)
  • 2020-11-21 12:06

    I assume that, since you have a generic class, you would have a variable like that:

    private T t;
    

    (this variable needs to take a value at the constructor)

    In that case you can simply create the following method:

    Class<T> getClassOfInstance()
    {
        return (Class<T>) t.getClass();
    }
    

    Hope it helps!

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