2

我已经阅读了 Get type of a generic parameter in Java with reflection post,这让我想知道这怎么可能。我使用了某人发布的解决方案并使用了代码

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 接口的类型定义。

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

4

3 回答 3

3

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

例如,如果您这样做:

class IntegerList extends ArrayList<Integer> {}

List<Integer> l = new IntegerList();

在这种情况下,您显示的代码实际上会返回Integer.class,因为Integer“烘焙”到IntegerList.

一些库(ab)通过使用类型标记来使用这个技巧。参见例如GSON 类TypeToken

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

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

这是因为这里创建的匿名类已经编译了它的类型参数是的信息Collection<Foo>

请注意,这是行不通的(即使TypeToken该类不会通过保护其构造函数来阻止它):

Type typeOfCollectionOfFoo = new TypeToken<Collection<Foo>>().getType()
于 2013-11-08T13:42:55.393 回答
2

javadoc 会告诉你你在做什么。

Class#getGenericSuperclass()状态

返回表示此 Class 所表示的实体(类、接口、原始类型或 void)的直接超类的 Type。

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

的直接超类ArrayListAbstractList。声明在源代码中就是这样

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类定义中使用的实际类型参数。

于 2013-11-08T13:47:30.247 回答
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. 唯一的好处是,您可以定义 T 的 getter 和 Setter,并且不需要一直对 Objects 进行类型转换。(因为 Java 正在为你做这件事)(它被称为类型擦除)

于 2013-11-08T13:44:48.680 回答