26

我见过类似的问题,但它们并没有太大帮助。

例如我有这个通用类:

public class ContainerTest<T>
{

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

和另一个使用这个容器类的类

public class TestCase
{

    private ContainerTest<String> containerTest;

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

是否可以在方法 doSomething() 中确定类型参数的类而无需在 ContainerTest 类中具有显式类型变量/字段或任何构造函数?

更新:更改了 ContainerTest 类的格式

4

7 回答 7

17

唯一的方法是将类存储在实例变量中,并将其作为构造函数的参数:

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

    public void doSomething()
    {
        //access tClass here
    }
}
于 2013-02-26T08:49:41.227 回答
10

如果您对反射方式感兴趣,我在这篇很棒的文章中找到了部分解决方案: http ://www.artima.com/weblogs/viewpost.jsp?thread=208860

简而言之,你可以使用java.lang.Class.getGenericSuperclass()java.lang.reflect.ParameterizedType.getActualTypeArguments()方法,但是你必须继承一些父超类。

以下代码段适用于直接扩展超类的类AbstractUserType。有关更通用的解决方案,请参阅参考文章。

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

}
于 2013-08-10T12:41:16.260 回答
7

没有“干净”的方法可以从类中获取通用类型参数。相反,一种常见的模式是将通用类型的类传递给构造函数,并将其作为内部属性保存,就像在 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

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.
    }
}
于 2013-02-26T08:50:08.193 回答
4

不,这是不可能的,因为类型擦除(类型参数编译为 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));
   }
}

利用:

ContainerTest<String> c = new ContainerTest<>(String.class);
于 2013-02-26T08:50:38.277 回答
2

一种TypeToken方法可以通过使用 Guava来捕获类型参数的运行时类型。该解决方案的缺点是每次需要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());
    }
}

TypeToken此解决方案的关键在上面代码中使用的构造函数的JavaDoc 中进行了描述:

客户端创建一个空的匿名子类。这样做会将类型参数嵌入到匿名类的类型层次结构中,因此我们可以在运行时重构它,尽管会被擦除。

于 2013-02-26T10:26:43.840 回答
1

要了解 ContainerTest 的价值,T您需要通过子类化 ContainerTest 在类型定义中捕获它:

class MyStringClass extends ContainerTest<String> {}

如果需要,您也可以使用匿名类来执行此操作:

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

然后要解析 T 的值,可以使用TypeTools

Class<?> stringType = TypeResolver.resolveRawArgument(ContainerTest.class, myStringClass.getClass());
assert stringType == String.class;
于 2013-10-26T02:40:52.290 回答
-1

如果你可以这样定义

public class ContainerTest<T>
{

    public void doSomething(T clazz)
    {

    }
}

那么有可能

于 2013-02-26T08:51:26.793 回答