Java泛型 - 获取实际类型的泛型参数

时间:2013-11-08 13:37:02

标签: java generics

我已阅读Get type of a generic parameter in Java with reflection帖子,这让我想知道如何做到这一点。我使用了某人发布并使用代码的解决方案

List<Integer> l = new ArrayList<>();
Class actualTypeArguments = (Class) ((ParameterizedType) l.getClass().getGenericSuperclass()).getActualTypeArguments()[0];

然而,这对我不起作用,导致

java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class

如果删除类强制类型,实际参数的类型为E,这是List接口的类型定义。

因此,我的问题是,我在这里做错了吗?这种行为无论如何都是我预期的,因为这些类型应该在编译期间被删除,对吗?

3 个答案:

答案 0 :(得分:2)

您使用的代码仅适用于某些特定情况,其中实际类型参数在编译时已知(并存储)。

例如,如果您这样做:

class IntegerList extends ArrayList<Integer> {}

List<Integer> l = new IntegerList();

在这种情况下,您展示的代码实际上会返回Integer.class,因为Integer已“加入”IntegerList

某些库(ab)通过使用类型令牌来使用此技巧。例如,请参阅GSON class TypeToken

  

表示通用类型T。您可以使用此类来获取类的泛型类型。 &GT;例如,要获取Collection<Foo>的泛型类型,可以使用:

Type typeOfCollectionOfFoo = new TypeToken<Collection<Foo>>(){}.getType()

这是有效的,因为在这里创建的匿名类已经编译了其类型参数为Collection<Foo>的信息。

请注意,这不起作用(即使TypeToken类不会通过使其构造函数受到保护而阻止它):

Type typeOfCollectionOfFoo = new TypeToken<Collection<Foo>>().getType()

答案 1 :(得分:2)

javadoc会告诉你你在做什么。

Class#getGenericSuperclass()

  

返回表示实体的直接超类的Type   (类,接口,基本类型或void)由此类表示。

     

如果超类是参数化类型,则返回Type对象   必须准确反映源中使用的实际类型参数   码。 [...]

ArrayList的直接超类是AbstractList。声明在源代码

中是这样的
public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

因此,如果您打印出它返回的Type对象,您将看到

java.util.AbstractList<E>

因此ParameterizedType#getActualTypeArguments()表示

  

返回表示实际类型的Type对象数组   这种类型的参数。

将返回Type

E

因为EArrayList类定义中使用的实际类型参数。

答案 2 :(得分:1)

您描述的方法仅在通用类型因继承而设置时才起作用,因为它在编译期间已知:

 public class SomeClass<T>{

 }

 public class SpecificClass extends SomeClass<String>{

 }

对于此示例,您可以使用该方法,然后您将返回“String.class”。

如果您正在动态创建实例 ,它将无效:

SomeClass s = new SomeClass<String>(); //wont work here.

一些常见的工作是,将实际的类作为参数传递给以后参考:

 public class SomeClass<T>{
    Class<T> clazz

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

    public Clazz<T> getGenericClass(){
       return this.clazz;
    } 
 }

用法:

 SomeClass<String> someClass= new SomeClass<String>(String.class);

 System.out.println(someClass.getGenericClass()) //String.class

实际上你甚至不需要Generic类型来实现这样的场景,因为Java会做同样的事情,就好像你将“T”作为Object处理一样。唯一的好处是,您可以定义getter和Setter of T,并且不需要一直强制对象。 (因为Java正在为你做这件事) (它被称为类型擦除)

相关问题