简体   繁体   English

如何在 Java 泛型中获取类型变量的类

[英]How to get the class of type variable in Java Generics

I've seen similar questions but they didnt help very much.我见过类似的问题,但它们并没有太大帮助。

For instance I've got this Generic Class:例如我有这个通用类:

public class ContainerTest<T>
{

    public void doSomething()
    {
        //I want here to determinate the Class of the type argument (In this case String)
    }
}

and Another Class which uses this Container Class和另一个使用这个容器类的类

public class TestCase
{

    private ContainerTest<String> containerTest;

    public void someMethod()
    {
        containerTest.doSomething();
    }
}

Is it possible to determinate the Class of the type argument in method doSomething() without having an explicit type variable/field or any constructor in ContainerTest Class?是否可以在没有显式类型变量/字段或 ContainerTest 类中的任何构造函数的情况下确定方法 doSomething() 中类型参数的类?

Update: Changed format of ContainerTest Class更新:更改了 ContainerTest 类的格式

The only way is to store the class in an instance variable and require it as an argument of the constructor: 唯一的方法是将类存储在实例变量中,并将其作为构造函数的参数:

public class ContainerTest<T>
{
    private Class<T> tClass;
    public ContainerTest(Class<T> tClass) {
        this.tCLass = tClass;
    }

    public void doSomething()
    {
        //access tClass here
    }
}

If you are interested in the reflection way , I found a partial solution in this great article: http://www.artima.com/weblogs/viewpost.jsp?thread=208860 如果您对反射方式感兴趣,我在这篇很棒的文章中找到了部分解决方案: http//www.artima.com/weblogs/viewpost.jsp?thread = 208860

In short, you can use java.lang.Class.getGenericSuperclass() and java.lang.reflect.ParameterizedType.getActualTypeArguments() methods, but you have to subclass some parent super class. 简而言之,您可以使用java.lang.Class.getGenericSuperclass()java.lang.reflect.ParameterizedType.getActualTypeArguments()方法,但您必须子类化一些父超类。

Following snippet works for a class that directly extends the superclass AbstractUserType . 以下代码段适用于直接扩展超类AbstractUserType的类。 See the referenced article for more general solution. 有关更一般的解决方案,请参阅参考文章

import java.lang.reflect.ParameterizedType;


public class AbstractUserType<T> {

    public Class<T> returnedClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass()
                .getGenericSuperclass();

        @SuppressWarnings("unchecked")
        Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0];

        return ret;
    }

    public static void main(String[] args) {
        AbstractUserType<String> myVar = new AbstractUserType<String>() {};

        System.err.println(myVar.returnedClass());
    }

}

There is no "clean" way to get the Generic Type argument from within the class. 从类中获取Generic Type参数没有“干净”的方法。 Instead, a common pattern is to pass the Class of the Generic Type to the constructor and keep it as an inner property juste as done in the java.util.EnumMap implementation. 相反,常见的模式是将通用类的类传递给构造函数,并将其作为内部属性juste保存,如java.util.EnumMap实现中所做的那样。

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk /6-b14/java/util/EnumMap.java

public class ContainerTest<T> {

    Class<T> type;
    T t;

    public ContainerTest(Class<T> type) {
        this.type = type;
    }

    public void setT(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public void doSomething() {
        //There you can use "type" property.
    }
}

No. It is not possible because of type erasure (the type parameters are compiled as Object + type casts). 不可以。因为类型擦除是不可能的(类型参数被编译为Object +类型转换)。 If you really need to know/enforce the type in runtime you may store a reference to a Class object. 如果您确实需要在运行时知道/强制执行该类型,则可以存储对Class对象的引用。

public class ContainerTest<T> {
   private final Class<T> klass;
   private final List<T> list = new ArrayList<T>();

   ContainerTest(Class<T> klass) {
     this.klass = klass;
   }

   Class<T> getElementClass() {
     return klass;
   }

   void add(T t) {
      //klass.cast forces a runtime cast operation
      list.add(klass.cast(t));
   }
}

Use: 使用:

ContainerTest<String> c = new ContainerTest<>(String.class);

To learn the value of T you'll need to capture it in a type definition by subclassing ContainerTest: 要了解T的值,您需要通过继承ContainerTest在类型定义中捕获它:

class MyStringClass extends ContainerTest<String> {}

You can also do this with an anonymous class if you want: 如果需要,您也可以使用匿名类执行此操作:

ContainerTest<String> myStringClass = new ContainerTest<String>{}();

Then to resolve the value of T, you can use TypeTools : 然后要解析T的值,您可以使用TypeTools

Class<?> stringType = TypeResolver.resolveRawArgument(ContainerTest.class, myStringClass.getClass());
assert stringType == String.class;

If You can define like this 如果您可以这样定义

public class ContainerTest<T>
{

    public void doSomething(T clazz)
    {

    }
}

Then it is possible 那么这是可能的

There is a way to get the runtime type of the type parameter by using Guava's TypeToken to capture it. 一种方法利用番石榴的获取类型参数的运行时类型TypeToken捕捉到它。 The solution's disadvantage is that you have to create an anonymous subclass each time you need an instance of Container . 解决方案的缺点是每次需要Container实例时都必须创建一个匿名子类。

class Container<T> {

    TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {};

    public Type getContainedType() {
        return tokenOfContainedType.getType();
    }
}

class TestCase {

    // note that containerTest is not a simple instance of Container,
    // an anonymous subclass is created
    private Container<String> containerTest = new Container<String>() {};

    @Test
    public void test() {
        Assert.assertEquals(String.class, containerTest.getContainedType());
    }
}

The key of this solution is described in tha JavaDoc of TypeToken 's constructor used in the code above: 在上面的代码中使用的TypeToken构造函数的JavaDoc中描述了此解决方案的关键:

Clients create an empty anonymous subclass. 客户端创建一个空的匿名子类。 Doing so embeds the type parameter in the anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure. 这样做会在匿名类的类型层次结构中嵌入type参数,因此我们可以在运行时重新构建它,尽管擦除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM