首页 文章

如何获取泛型类型T的类实例

提问于
浏览
556

我有一个泛型类, Foo<T> . 在 Foo 的方法中,我想得到类型为T的类实例,但我不能调用 T.class .

使用 T.class 绕过它的首选方法是什么?

17 回答

  • 0

    简而言之,没有办法在Java中找出泛型类型参数的运行时类型 . 我建议阅读Java Tutorial中关于类型擦除的章节以获取更多详细信息 .

    一种流行的解决方案是将类型参数的 Class 传递给泛型类型的构造函数,例如:

    class Foo<T> {
        final Class<T> typeParameterClass;
    
        public Foo(Class<T> typeParameterClass) {
            this.typeParameterClass = typeParameterClass;
        }
    
        public void bar() {
            // you can access the typeParameterClass here and do whatever you like
        }
    }
    
  • 9

    这是一个有效的解决方案:

    @SuppressWarnings("unchecked")
    private Class<T> getGenericTypeClass() {
        try {
            String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
            Class<?> clazz = Class.forName(className);
            return (Class<T>) clazz;
        } catch (Exception e) {
            throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
        }
    }
    

    注意: Can be used only as superclass

    • 必须使用类型化类扩展( Child extends Generic<Integer>

    OR

    • 必须创建为匿名实现( new Generic<Integer>() {};
  • 473

    比其他人建议的类更好的路径是传入一个对象,该对象可以执行您对该类所做的操作,例如,创建一个新实例 .

    interface Factory<T> {
      T apply();
    }
    
    <T> void List<T> make10(Factory<T> factory) {
      List<T> result = new ArrayList<T>();
      for (int a = 0; a < 10; a++)
        result.add(factory.apply());
      return result;
    }
    
    class FooFactory<T> implements Factory<Foo<T>> {
      public Foo<T> apply() {
        return new Foo<T>();
      }
    }
    
    List<Foo<Integer>> foos = make10(new FooFactory<Integer>());
    
  • 186

    由于类型擦除,你不能这样做 . 另请参阅Stack Overflow问题Java generics - type erasure - when and what happens .

  • 17

    我发现了一种通用且简单的方法 . 在我的课程中,我创建了一个方法,根据它在类定义中的位置返回泛型类型 . 我们假设一个类定义如下:

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

    现在让我们创建一些属性来保存类型:

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

    然后,您可以创建一个泛型方法,该方法根据泛型定义的索引返回类型:

    /**
         * 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];
        }
    

    最后,在构造函数中,只需调用方法并为每种类型发送索引 . 完整的代码应如下所示:

    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];
        }
    }
    
  • 1

    这是可能的:

    class Foo<T> {
      Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
    }
    

    您需要svn/trunk/dao/src/main/java/com/googlecode/genericdao/dao/ DAOUtil.java中的两个函数 .

    有关更多说明,请参阅Reflecting generics .

  • 7

    想象一下,你有一个通用的抽象超类:

    public abstract class Foo<? extends T> {}
    

    然后你有第二个类扩展了Foo,扩展了T:

    public class Second extends Foo<Bar> {}
    

    您可以通过选择 Type (来自bert bruynooghe答案)并使用 Class 实例推断它来获取Foo类中的类 Bar.class

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

    您必须注意此操作并不理想,因此最好缓存计算值以避免对此进行多次计算 . 其中一个典型用途是通用DAO实现 .

    最终实施:

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

    从其他函数或Bar类中的Foo类调用时,返回的值为Bar.class .

  • 2

    我在抽象泛型类中遇到了这个问题 . 在这种特殊情况下,解决方案更简单:

    abstract class Foo<T> {
        abstract Class<T> getTClass();
        //...
    }
    

    以及后来的派生类:

    class Bar extends Foo<Whatever> {
        @Override
        Class<T> getTClass() {
            return Whatever.class;
        }
    }
    
  • -4

    实际上,我想你的T类中有一个字段 . 如果没有T类字段,那么有一个泛型类型有什么意义呢?因此,您只需在该字段上执行instanceof即可 .

    就我而言,我有一个

    List<T> items;
    

    在我班上,我检查 class 类型是否为"Locality"

    if (items.get(0) instanceof Locality) ...
    

    当然,这仅在可能的类的总数有限时才有效 .

  • 34

    我有一个(丑陋但有效)解决这个问题的方法,我最近用过:

    import java.lang.reflect.TypeVariable;
    
    
    public static <T> Class<T> getGenericClass()
    {
        __<T> ins = new __<T>();
        TypeVariable<?>[] cls = ins.getClass().getTypeParameters(); 
    
        return (Class<T>)cls[0].getClass();
    }
    
    private final class __<T> // generic helper class which does only provide type information
    {
        private __()
        {
        }
    }
    
  • 82
    public <T> T yourMethodSignature(Class<T> type) {
    
            // get some object and check the type match the given type
            Object result = ...            
    
            if (type.isAssignableFrom(result.getClass())) {
                return (T)result;
            } else {
                // handle the error
            }
       }
    
  • 4

    我正在使用解决方法:

    class MyClass extends Foo<T> {
    ....
    }
    
    MyClass myClassInstance = MyClass.class.newInstance();
    
  • 1

    如果要扩展或实现使用泛型的任何类/接口,则可以获得父类/接口的通用类型,而无需修改任何现有的类/接口 .

    可能有三种可能性,

    Case 1 当您的类正在扩展使用泛型的类时

    public class TestGenerics {
        public static void main(String[] args) {
            Type type = TestMySuperGenericType.class.getGenericSuperclass();
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
    
    class GenericClass<T> {
        public void print(T obj){};
    }
    
    class TestMySuperGenericType extends GenericClass<Integer> {
    }
    

    Case 2 当您的类正在实现使用Generics的接口时

    public class TestGenerics {
        public static void main(String[] args) {
            Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
            for(Type type : interfaces){
                Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
                for(Type gType : gTypes){
                    System.out.println("Generic type:"+gType.toString());
                }
            }
        }
    }
    
    interface GenericClass<T> {
        public void print(T obj);
    }
    
    class TestMySuperGenericType implements GenericClass<Integer> {
        public void print(Integer obj){}
    }
    

    Case 3 当您的界面正在扩展使用Generics的界面时

    public class TestGenerics {
        public static void main(String[] args) {
            Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
            for(Type type : interfaces){
                Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
                for(Type gType : gTypes){
                    System.out.println("Generic type:"+gType.toString());
                }
            }
        }
    }
    
    interface GenericClass<T> {
        public void print(T obj);
    }
    
    interface TestMySuperGenericType extends GenericClass<Integer> {
    }
    
  • 8

    正如在其他答案中所解释的那样,要使用这个 ParameterizedType 方法,你需要扩展类,但这似乎是额外的工作来创建一个扩展它的全新类...

    因此,使类抽象它会强制您扩展它,从而满足子类化要求 . (使用lombok的@Getter) .

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

    现在扩展它而不定义新类 . (注意最后的{} ...扩展,但不要覆盖任何东西 - 除非你想要) .

    private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
    private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
    ...
    Class stringType = myConfigA.getType();
    Class fileType = myConfigB.getType();
    
  • 11

    标准方法/变通方法/解决方案是将 class 对象添加到构造函数中,如:

    public class Foo<T> {
    
        private Class<T> type;
        public Foo(Class<T> type) {
          this.type = type;
        }
    
        public Class<T> getType() {
          return type;
        }
    
        public T newInstance() {
          return type.newInstance();
        }
     }
    
  • 4

    但是有一个小漏洞:如果你将 Foo 类定义为抽象 . 这意味着你必须将你的类实例化为:

    Foo<MyType> myFoo = new Foo<MyType>(){};
    

    (注意最后的双括号 . )

    现在,您可以在运行时检索 T 的类型:

    Type mySuperclass = myFoo.getClass().getGenericSuperclass();
    Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
    

    但请注意, mySuperclass 必须是实际定义 T 的最终类型的类定义的超类 .

    它也不是很优雅,但您必须在代码中决定是否更喜欢 new Foo<MyType>(){}new Foo<MyType>(MyType.class); .


    例如:

    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    
    import java.util.ArrayDeque;
    import java.util.Deque;
    import java.util.NoSuchElementException;
    
    /**
     * Captures and silently ignores stack exceptions upon popping.
     */
    public abstract class SilentStack<E> extends ArrayDeque<E> {
      public E pop() {
        try {
          return super.pop();
        }
        catch( NoSuchElementException nsee ) {
          return create();
        }
      }
    
      public E create() {
        try {
          Type sooper = getClass().getGenericSuperclass();
          Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];
    
          return (E)(Class.forName( t.toString() ).newInstance());
        }
        catch( Exception e ) {
          return null;
        }
      }
    }
    

    然后:

    public class Main {
        // Note the braces...
        private Deque<String> stack = new SilentStack<String>(){};
    
        public static void main( String args[] ) {
          // Returns a new instance of String.
          String s = stack.pop();
          System.out.printf( "s = '%s'\n", s );
        }
    }
    
  • 3

    我一直在寻找一种方法来自己做,而不需要在类路径上添加额外的依赖 . 经过一些调查后,我发现只要你有一个通用的超类型就可以.168189_ . 这对我来说没问题我正在使用具有通用图层超类型的DAO图层 . 如果这适合你的情况那么这是最好的方法恕我直言 .

    我遇到的大多数泛型用例具有某种通用超类型,例如: List<T> 代表 ArrayList<T>GenericDAO<T> 代表 DAO<T>

    纯Java解决方案

    文章Accessing generic types at runtime in Java解释了如何使用纯Java来实现它 .

    Spring解决方案

    我的项目使用的是Spring,这更好,因为Spring有一个方便的实用方法来查找类型 . 这对我来说是最好的方法,因为它看起来最好 . 我想如果你没有使用Spring,你可以编写自己的实用方法 .

    import org.springframework.core.GenericTypeResolver;
    
    public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
    {
    
        @Autowired
        private SessionFactory sessionFactory;
    
        private final Class<T> genericType;
    
        private final String RECORD_COUNT_HQL;
        private final String FIND_ALL_HQL;
    
        @SuppressWarnings("unchecked")
        public AbstractHibernateDao()
        {
            this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
            this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
            this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
        }
    

相关问题