2011-07-08 262 views
22

在使用泛型时,java中存在一个小问题。我有一个类A获取泛型类的泛型参数的类型名称

public class A<T> 

A的方法,我需要得到的T类型名称。 有没有办法找到字符串s使用T? (如果我创建A<String> temp = new A<String>();,我希望能够得到java.lang.String在一个点 - 我必须使用泛型,因为我的方法之一将不得不返回List<T>)。

这似乎很容易,但我不知道该怎么做。

非常感谢您的帮助。

+2

看到这个问题http://stackoverflow.com/questions/3403909/get-generic-type-of-class-at-runtime –

回答

34

你不能在一般的做到这一点,因为类型擦除的 - 的A<String>实例不会知道T类型。如果你需要它,一个方法是使用一种类型的文字:

public class A<T> 
{ 
    private final Class<T> clazz; 

    public A<T>(Class<T> clazz) 
    { 
     this.clazz = clazz; 
    } 

    // Use clazz in here 
} 

然后:

A<String> x = new A<String>(String.class); 

这是丑陋的,但是这确实:(

另一种方法是使用什么类型擦除有点像Guice的TypeLiteral。这是有效的,因为用于指定超类的类型参数没有被删除,所以你可以这样做:

A<String> a = new A<String>() {}; 

a现指子类的A<String>,因此受到越来越a.getClass().getSuperClass()你最终可以回到String。虽然这很可怕。

0

Java中的泛型是通过擦除来实现的,所以不会,您将无法获得用于在运行时创建泛型集合的“类型”的名称。另外,为什么不只是检查元素以知道它属于哪种类型?

+0

其实我不能,因为我在开始时没有任何T的实例。该对象实现了一个osgi Service Listener,我想用我的接口名称来过滤bundle消息。之后我才得到实例。 – ThR37

9

简答:不可能。

稍微长一点的答案:编译代码后,类型参数将被丢弃。因此,Java不知道你在那里设置了什么。 你可以,但是,通过有问题的类的对象,并对其进行操作:

public class A<T> { 
    private final clazz; 

    A(Class<T> clazz){ 
    this.clazz = clazz; 
    } 
... 
} 
+0

这肯定是答案。由于*擦除*(这是我经常听到的一个术语,但其操作我从未完全深入分析),因此永远不能使用通用参数作为“走私”额外参数。相反,使用常规参数。似乎很奇怪的是,这些信息永远无法获得,我不清楚这是设计选择还是不可避免的... –

5

这当然是可能的,只要指定类型参数通过A的子类:

public class B extends A<String> {} 

Class<?> typeArg = TypeResolver.resolveRawArgument(A.class, B.class); 
assert typeArg == String.class; 

TypeResolver可通过TypeTools获得。

+0

我真的希望这可以提供答案并获得代码。但是我不能做OP想要做的事情:从“实时”创建的A实例中获取泛型参数,即不实际创建子类。我认为这是不可能的... –

16

您可以从子类中获得泛型的名称。看到这个例子。 我们定义了一个父类是这样的:

public class GetTypeParent<T> { 

    protected String getGenericName() 
    { 
     return ((Class<T>) ((ParameterizedType) getClass() 
       .getGenericSuperclass()).getActualTypeArguments()[0]).getTypeName(); 
    } 
} 

然后,我们用这种方式定义它的子类:

public class GetTypeChild extends GetTypeParent<String> { 
    public static void main(String[] args) { 
     GetTypeChild getTypeChild = new GetTypeChild(); 
     System.out.println(getTypeChild.getGenericName()); 
    } 
} 

你可以看到,在主要方法,或以任何实例方法,我能够获得泛型类型的名称,在这种情况下,main将打印:java.lang.String。

+1

...并且你得到子类的名字(如果有的话)。太好了! –

+1

这看起来确实很聪明......但是你必须“硬编码”类型'String'才能得到'String'。是不是试图找到泛型参数的值的问题? –

+0

我不认为这有帮助。子类已经知道该类型,因为它是硬编码的:String。调用父级的方法来获取它是没有用的。不幸的是,它不是一个有用的用例,它不是一个有用的用例,而且它不像@mikeodent –

1

如果你在它有它的定义泛型类型父类的子类这样做,这是对我工作:

// get generic type class name 
String name = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].toString(); 

// then when you've got the name, you can make the Class<T> object 
Class.forName(name.replace("class ", "")) 

原因,我无法#getClass()做到这一点,而不是#toString()在第一个剪辑中,我总是得到“阶级”班,这对我来说毫无用处。

0

由于通常是这种情况,Apache的具有用于这一个与TypeUtils的溶液:

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/TypeUtils.html

从上述问题一个简单的例子:

TypeUtils.getTypeArguments(temp.getClass(), A.class).get(A.class.getTypeParameters()[0]) 

声明:我没有试图首先构建,但过去曾以类似的方式使用该实用程序。

+0

Apache Commons的粉丝,从来没有听说过'TypeUtils',所以谢谢......但是目前这对我不起作用:'A.class'给出的错误是“类型参数A的非法类文字”。 。只是要用'TypeUtils'做一些探索/实验...... –

+0

@mike啮齿动物这个对我来说很有用,当我用它在分水岭和不同类型的多个类中移动时通过水线。你有没有可用的例子吗? – Hazok

+0

后来:不行,不能得到这个工作。不知道如何首先获得'ParameterizedType'。还看了Jonathan的回答和Enrico Guerin的回答。但是从实例直接获取泛型类型(OP的问题)似乎是不可能的,即使对于Apache也是如此。除非你能告诉我如何! –