简体   繁体   English

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

[英]Java generics - obtaining actual type of generic parameter

I have read Get type of a generic parameter in Java with reflection post and it made me wonder how that would be possible.我已经通过反射帖子阅读了 Java 中泛型参数的获取类型,这让我想知道这怎么可能。 I used the solution that someone posted and using the code我使用了有人发布并使用代码的解决方案

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

This, however does not work for me, resulting in然而,这对我不起作用,导致

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

If I remove the class cast, the type of the actual argument is E , which is the type definition from List interface.如果我删除类强制转换,则实际参数的类型是E ,它是 List 接口的类型定义。

My question is, therefore, am I doing something wrong here?因此,我的问题是,我在这里做错了吗? This behaviour is something I would have expected anyway, since the types are supposed to be erased during compile time, correct?无论如何,这种行为是我所期望的,因为这些类型应该在编译时被擦除,对吗?

The code you use only works in some very specific cases, where the actual type parameter is known (and stored) at compile time.您使用的代码仅适用于某些非常特定的情况,在这些情况下,实际类型参数在编译时是已知的(并存储)。

For example if you did this:例如,如果你这样做:

class IntegerList extends ArrayList<Integer> {}

List<Integer> l = new IntegerList();

In this case the code you showed would actually return Integer.class , because Integer is "baked into" the IntegerList .在这种情况下,您显示的代码实际上会返回Integer.class ,因为Integer被“烘焙到” IntegerList

Some libraries (ab)use this trick via the use of type tokens.一些库 (ab) 通过使用类型标记来使用这个技巧。 See for example the GSON class TypeToken :例如参见GSON 类TypeToken

Represents a generic type T .表示泛型类型T You can use this class to get the generic type for a class.您可以使用此类来获取类的泛型类型。 > For example, to get the generic type for Collection<Foo> , you can use: > 例如,要获取Collection<Foo>的泛型类型,您可以使用:

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

This works because the anonymous class created in here has compiled-in the information that its type parameter is Collection<Foo> .这是有效的,因为这里创建的匿名类已经编译了它的类型参数是Collection<Foo>

Note that this would not work (even if the TypeToken class wouldn't prevent it by making its constructor protected):请注意,这不起作用(即使TypeToken类不会通过保护其构造函数来阻止它):

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

The javadoc will tell you what you are doing. javadoc 会告诉你你在做什么。

Class#getGenericSuperclass() states Class#getGenericSuperclass()状态

Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.返回表示由此类表示的实体(类、接口、原始类型或 void)的直接超类的类型。

If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code.如果超类是参数化类型,则返回的 Type 对象必须准确反映源代码中使用的实际类型参数。 [...] [...]

The direct superclass of ArrayList is AbstractList . ArrayList的直接超类是AbstractList The declaration is as such in the source code源代码中的声明是这样的

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

So if you print out the Type object returned by it, you will see所以如果你打印出它返回的Type对象,你会看到

java.util.AbstractList<E>

and therefore ParameterizedType#getActualTypeArguments() which states因此ParameterizedType#getActualTypeArguments()指出

Returns an array of Type objects representing the actual type arguments to this type.返回一个 Type 对象数组,表示该类型的实际类型参数。

will return the Type将返回Type

E

since E is the actual type argument used in the ArrayList class definition.因为EArrayList类定义中使用的实际类型参数。

The method you described does ONLY work, when the Generic Type is Set due to inheritance, because then its known during compile time:您描述的方法仅在由于继承而设置通用类型时才有效,因为在编译时它是已知的:

 public class SomeClass<T>{

 }

 public class SpecificClass extends SomeClass<String>{

 }

For this example, you can use the method and you'll get back "String.class".对于这个例子,你可以使用这个方法,你会得到“String.class”。

If you are creating instances on the fly it won't work:如果您正在即时创建实例它将不起作用:

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

Some common work around is, to pass the actual class as a parameter for later reference:一些常见的解决方法是,将实际类作为参数传递以供以后参考:

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

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

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

usage:用法:

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

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

Actually you don't even need the Generic type for such an scenario, because Java would do the same thing, as if you would handle the "T" as Object .实际上,对于这种情况,您甚至不需要 Generic 类型,因为 Java 会做同样的事情,就好像您将“T”作为Object处理一样。 Only advantage is, that you can define getter and Setter of T and don't need to typecast Objects all the time.唯一的优点是,您可以定义 T 的 getter 和 Setter,并且不需要一直对 Objects 进行类型转换。 (Because Java is doing that for you) (It's called Type Erasure) (因为 Java 正在为你做这件事)(它被称为类型擦除)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM