2013-02-26 191 views
16

我见过类似的问题,但他们没有太大的帮助。如何在Java中获取类型变量的类型泛型

比如我有这个泛型类:

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类

+0

'if(t instanceof String)'? – vikingsteve 2013-02-26 08:41:40

+0

我已经更新了这个问题 – ZeDonDino 2013-02-26 08:44:48

+0

你不能只将类类型作为参数传递吗? – 2013-02-26 08:47:05

回答

12

唯一的办法改变了形式是对类存储在一个实例变量,并要求其作为构造函数的参数:

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

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

这正是我想要做的。 – 2013-02-26 09:02:30

+2

你确定这是唯一的方法吗? – vach 2014-05-06 06:37:05

+0

除非你强制调用者继承你的班级,否则。但它通常不是问题... – 2014-05-06 11:15:54

5

没有“干净”从类中获取泛型类型参数的方法。 相反,一种常见模式是将泛型类的类传递给构造函数,并将其作为内部属性对齐,如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. 
    } 
} 
+0

你为什么要链接EnumMap? – ZeDonDino 2013-02-26 08:51:59

+0

这被称为“类型令牌” – hertzsprung 2013-02-26 08:54:01

+1

@ZeDonDino我链接EnumMap以显示即使OpenJDK在standrad实现中也使用这种模式。 – RenaudBlue 2013-02-26 10:08:43

2

号这是不可能的,因为type erasure(类型参数被编译为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); 
+2

请谨慎使用,仅在Java SE 7及更高版本中支持。 – ZeDonDino 2013-02-26 08:55:44

+0

ContainerTest c = new ContainerTest (String.class) //所以钻石诞生了 – Javier 2013-02-26 08:57:29

+0

嗯,我知道这一点。我希望有一种方法可以将类从中取出,而不是将值传递给构造函数或字段或类似的方法。 – ZeDonDino 2013-02-26 09:04:34

0

如果你可以这样定义

public class ContainerTest<T> 
{ 

    public void doSomething(T clazz) 
    { 

    } 
} 

那么很可能

+0

我想在不带任何参数的情况下调用此方法。 – ZeDonDino 2013-02-26 08:54:36

0

一种方式来获得的运行时类型键入参数,使用Guava的TypeToken来捕获它。该解决方案的缺点是,每次需要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的构造的THA JavaDoc的描述:

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

+0

哇,一个downvote,这很好...你至少可以评论你为什么不喜欢它? – zagyi 2013-02-26 11:25:33

+0

这是一个有趣的解决方案,但我认为它会更适合像番石榴那样的反射,而不是这个问题的用例。这个解决方案的问题在于它需要在实例化它的每个位置创建一个匿名类。 (注意我不是downvoter) – 2013-02-26 12:38:00

8

如果你有兴趣在反射方式,我发现这篇大文章中的部分解决方案: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()); 
    } 

} 
+0

这对我有效。谢谢。 – aBarocio80 2015-01-09 16:37:17

1

要了解的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;