Java 如何获得泛型类型 T 的类实例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3437897/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 00:47:49  来源:igfitidea点击:

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

javagenerics

提问by robinmag

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

我有一个泛型类,Foo<T>. 在 的方法中Foo,我想获取 type 的类实例T,但我无法调用T.class.

What is the preferred way to get around it using T.class?

使用 解决它的首选方法是什么T.class

采纳答案by Zsolt T?r?k

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. I suggest reading the chapter about type erasure in the Java Tutorialfor more details.

简短的回答是,无法在 Java 中找出泛型类型参数的运行时类型。我建议阅读Java 教程中有关类型擦除的章节以获取更多详细信息。

A popular solution to this is to pass the Classof the type parameter into the constructor of the generic type, e.g.

一个流行的解决方案是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
    }
}

回答by Konrad Garus

You can't do it because of type erasure. See also Stack Overflow question Java generics - type erasure - when and what happens.

由于类型擦除,您不能这样做。另请参阅堆栈溢出问题Java 泛型 - 类型擦除 - 发生的时间和情况

回答by Andreas Dolk

A standard approach/workaround/solution is to add a classobject to the constructor(s), like:

标准方法/解决方法/解决方案是向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();
    }
 }

回答by Ricky Clarkson

A better route than the Class the others suggested is to pass in an object that can do what you would have done with the Class, e.g., create a new instance.

比其他人建议的 Class 更好的方法是传入一个对象,该对象可以执行您对 Class 执行的操作,例如,创建一个新实例。

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

回答by bert bruynooghe

There is a small loophole however: if you define your Fooclass as abstract. That would mean you have to instantiate you class as:

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

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

(Note the double braces at the end.)

(注意末尾的双括号。)

Now you can retrieve the type of Tat runtime:

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

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

Note however that mySuperclasshas to be the superclass of the class definition actually defining the final type for T.

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

It is also not very elegant, but you have to decide whether you prefer new Foo<MyType>(){}or new Foo<MyType>(MyType.class);in your code.

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



For example:

例如:

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

Then:

然后:

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

回答by Christine

Actually, I suppose you have a field in your class of type T. If there's no field of type T, what's the point of having a generic Type? So, you can simply do an instanceof on that field.

实际上,我假设您的类中有一个 T 类型的字段。如果没有 T 类型的字段,那么拥有泛型 Type 有什么意义?所以,你可以简单地在那个字段上做一个 instanceof 。

In my case, I have a

就我而言,我有一个

List<T> items;
在我的班级中,我检查班级类型是否为“Locality”

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

Of course, this only works if the total number of possible classes is limited.

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

回答by Ben Thurley

I was looking for a way to do this myself without adding an extra dependency to the classpath. After some investigation I found that it ispossible as long as you have a generic supertype. This was OK for me as I was working with a DAOlayer with a generic layer supertype. If this fits your scenario then it's the neatest approach IMHO.

我正在寻找一种方法来自己完成此操作,而无需向类路径添加额外的依赖项。经过一番调查,我发现,这可能的,只要你有一个通用的超类型。这对我来说没问题,因为我正在使用具有通用层超类型的DAO层。如果这适合您的情况,那么恕我直言,这是最简洁的方法。

Most generics use cases I've come across have some kind of generic supertype e.g. List<T>for ArrayList<T>or GenericDAO<T>for DAO<T>, etc.

我遇到的大多数泛型用例都有某种泛型超类型,例如List<T>forArrayList<T>GenericDAO<T>forDAO<T>等。

Pure Java solution

纯Java解决方案

The article Accessing generic types at runtime in Javaexplains how you can do it using pure Java.

这篇文章中的Java运行时访问泛型类型介绍如何使用纯Java做到这一点。

@SuppressWarnings("unchecked")
public GenericJpaDao() {
  this.entityBeanType = ((Class) ((ParameterizedType) getClass()
      .getGenericSuperclass()).getActualTypeArguments()[0]);
}

Spring solution

弹簧解决方案

My project was using Springwhich is even better as Spring has a handy utility method for finding the type. This is the best approach for me as it looks neatest. I guess if you weren't using Spring you could write your own utility method.

我的项目使用的是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 ";
    }

回答by user1154664

I had this problem in an abstract generic class. In this particular case, the solution is simpler:

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

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

and later on the derived class:

后来在派生类上:

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

回答by Peter Tseng

It's possible:

这是可能的:

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

You need two functions from hibernate-generic-dao/blob/master/dao/src/main/java/com/googlecode/genericdao/dao/DAOUtil.java.

您需要hibernate-generic-dao/blob/master/dao/src/main/java/com/googlecode/genericdao/dao/DAOUtil.java 中的两个函数

For more explanations, see Reflecting generics.

有关更多解释,请参阅反映泛型

回答by droidpl

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:

然后你有第二个类,它使用一个扩展 T 的泛型 Bar 扩展 Foo:

public class Second extends Foo<Bar> {}

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

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

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.

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

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.

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