关于java:如何获取泛型类型T的类实例

How to get a class instance of generics type T

我有一个仿制药班,Foo。在Foo的一个方法中,我想得到t类型的类实例,但是我不能调用T.class

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


简短的答案是,没有办法在Java中找出泛型类型参数的运行时类型。我建议在Java教程中阅读关于擦除的章节以获得更多的细节。

对此,一种流行的解决方案是将类型参数的Class传递到泛型类型的构造函数中,例如。

1
2
3
4
5
6
7
8
9
10
11
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
    }
}


我在寻找一种不增加对类路径的额外依赖性的方法。经过一些调查,我发现只要有一个泛型父类型,就有可能。这对我来说没问题,因为我正在使用一个带有通用层超类型的DAO层。如果这符合你的情况,那就是最整洁的方法。

我遇到的大多数泛型用例都有某种泛型超类型,例如,List用于ArrayListGenericDAO用于DAO等。

纯Java解决方案

在Java中运行时访问泛型类型的文章解释了如何使用纯Java来实现它。

弹簧解

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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";
    }


不过,还有一个小漏洞:如果您将Foo类定义为抽象类。这意味着您必须将类实例化为:

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

(注意两端的双大括号。)

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

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

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

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

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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;
    }
  }
}

然后:

1
2
3
4
5
6
7
8
9
10
11
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'
"
, s );
    }
}


标准方法/解决方案/解决方案是向构造函数添加一个Class对象,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 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();
    }
 }


假设您有一个通用的抽象超类:

1
public abstract class Foo<? extends T> {}

然后有第二个类用扩展t的泛型条扩展foo:

1
public class Second extends Foo<Bar> {}

您可以在foo类中选择Type(来自bert bruynooghe答案),并使用Class实例进行推断,得到类Bar.class

1
2
3
4
5
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实现中。

最终实施:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
    }
}

当从其他函数中的foo类或bar类调用时,返回的值为bar.class。


以下是一个有效的解决方案:

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

笔记:只能用作超类

  • 必须用类型类(Child extends Generic扩展)
  • 必须创建为匿名实现(new Generic() {};)

  • 你不能这样做,因为类型删除。参见堆栈溢出问题Java泛型-类型擦除-什么时候发生什么。


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

    1
    2
    3
    4
    abstract class Foo<T> {
        abstract Class<T> getTClass();
        //...
    }

    在派生类的后面:

    1
    2
    3
    4
    5
    6
    class Bar extends Foo<Whatever> {
        @Override
        Class<T> getTClass() {
            return Whatever.class;
        }
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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>());


    对于这个问题,我有一个(丑陋但有效的)解决方案,我最近使用它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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 __()
        {
        }
    }

    这是可能的:

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

    您需要从Sv/Base/Daa/Src/Ma/Java/COM/GooGeCord/GuangiDaa/Daou/DaouTur.java两个函数。

    有关更多解释,请参见反射泛型。


    我找到了一个通用的简单方法。在我的类中,我创建了一个方法,该方法根据泛型类型在类定义中的位置返回它。让我们假设一个这样的类定义:

    1
    2
    3
    public class MyClass<A, B, C> {

    }

    现在,让我们创建一些属性来持久化这些类型:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class MyClass<A, B, C> {

        private Class<A> aType;

        private Class bType;

        private Class<C> cType;

    // Getters and setters (not necessary if you are going to use them internally)

        }

    然后,可以创建一个基于泛型定义索引返回类型的泛型方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
       /**
         * 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];
        }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class MyClass<A, B, C> {

        private Class<A> aType;

        private Class bType;

        private Class<C> cType;


        public MyClass() {
          this.aType = (Class<A>) getGenericClassType(0);
          this.bType = (Class) 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];
        }
    }

    如其他答案所述,要使用这种ParameterizedType方法,您需要扩展类,但这似乎是额外的工作,以创建一个扩展类的全新类…

    因此,使类抽象化它迫使您扩展它,从而满足子类化的要求。(使用Lombok的@getter)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Getter
    public abstract class ConfigurationDefinition<T> {

        private Class<T> type;
        ...

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

    现在扩展它而不定义新类。(注意结尾的…扩展,但不要覆盖任何内容-除非您愿意)。

    1
    2
    3
    4
    5
    private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
    private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
    ...
    Class stringType = myConfigA.getType();
    Class fileType = myConfigB.getType();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       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
            }
       }

    如果您正在扩展或实现任何使用泛型的类/接口,那么您可以获得父类/接口的泛型类型,而不必修改任何现有的类/接口。

    可能有三种可能性,

    案例1当类扩展使用泛型的类时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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> {
    }

    案例2当类正在实现使用泛型的接口时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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){}
    }

    案例3当接口扩展使用泛型的接口时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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> {
    }

    实际上,我认为在T类型的类中有一个字段。如果没有T类型的字段,那么拥有一个泛型类型有什么意义呢?所以,您可以简单地在该字段上执行一个实例。

    在我的例子中,我在我的班级里有一个

    1
    List<T> items;

    ,我通过以下方式检查班级类型是否为"地区"。

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

    当然,只有在可能类的总数有限的情况下,这才有效。


    我正在为此使用变通方法:

    1
    2
    3
    4
    5
    class MyClass extends Foo<T> {
    ....
    }

    MyClass myClassInstance = MyClass.class.newInstance();