Java泛型——获取实际类型的泛型参数。

时间:2023-02-08 16:32:35

I have read Get type of a generic parameter in Java with reflection post and it made me wonder how that would be possible. I used the solution that someone posted and using the code

我已经阅读了Java中带有反射贴子的泛型参数的Get类型,这让我想知道这是怎么可能的。我使用了有人发布和使用代码的解决方案。

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.

如果我删除了类cast,那么实际参数的类型是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?

我的问题是,我在这里做错了什么吗?这种行为是我应该预料到的,因为这些类型应该在编译时被删除,对吗?

3 个解决方案

#1


2  

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.

在这种情况下,您所展示的代码实际上会返回整数。类,因为整数被“烤成”整数。

Some libraries (ab)use this trick via the use of type tokens. See for example the GSON class TypeToken:

一些库(ab)通过使用类型令牌来使用这个技巧。例如GSON类类型标记:

Represents a generic type 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:

表示一个泛型类型。您可以使用这个类来获得类的泛型类型。例如,为了获取集合 的泛型类型,您可以使用:

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 的信息。

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()

#2


2  

The javadoc will tell you what you are doing.

javadoc将告诉您正在做什么。

Class#getGenericSuperclass() states

类# 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. [...]

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

The direct superclass of ArrayList is AbstractList. The declaration is as such in the source code

ArrayList的直接超类是AbstractList。该声明在源代码中是这样的。

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

因此,如果打印返回的类型对象,您将看到。

java.util.AbstractList<E>

and therefore ParameterizedType#getActualTypeArguments() which states

因此,参数化类型#getActualTypeArguments()是哪些状态。

Returns an array of Type objects representing the actual type arguments to this type.

返回表示该类型的实际类型参数的类型对象数组。

will return the Type

将返回类型

E

since E is the actual type argument used in the ArrayList class definition.

因为E是ArrayList类定义中使用的实际类型参数。

#3


1  

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. Only advantage is, that you can define getter and Setter of T and don't need to typecast Objects all the time. (Because Java is doing that for you) (It's called Type Erasure)

实际上,您甚至不需要这种场景的泛型类型,因为Java会做同样的事情,就像处理“T”一样。唯一的优势是,您可以定义getter和Setter,并且不需要一直对对象进行类型转换。(因为Java正在为你这样做)(这被称为类型删除)

#1


2  

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.

在这种情况下,您所展示的代码实际上会返回整数。类,因为整数被“烤成”整数。

Some libraries (ab)use this trick via the use of type tokens. See for example the GSON class TypeToken:

一些库(ab)通过使用类型令牌来使用这个技巧。例如GSON类类型标记:

Represents a generic type 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:

表示一个泛型类型。您可以使用这个类来获得类的泛型类型。例如,为了获取集合 的泛型类型,您可以使用:

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 的信息。

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()

#2


2  

The javadoc will tell you what you are doing.

javadoc将告诉您正在做什么。

Class#getGenericSuperclass() states

类# 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. [...]

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

The direct superclass of ArrayList is AbstractList. The declaration is as such in the source code

ArrayList的直接超类是AbstractList。该声明在源代码中是这样的。

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

因此,如果打印返回的类型对象,您将看到。

java.util.AbstractList<E>

and therefore ParameterizedType#getActualTypeArguments() which states

因此,参数化类型#getActualTypeArguments()是哪些状态。

Returns an array of Type objects representing the actual type arguments to this type.

返回表示该类型的实际类型参数的类型对象数组。

will return the Type

将返回类型

E

since E is the actual type argument used in the ArrayList class definition.

因为E是ArrayList类定义中使用的实际类型参数。

#3


1  

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. Only advantage is, that you can define getter and Setter of T and don't need to typecast Objects all the time. (Because Java is doing that for you) (It's called Type Erasure)

实际上,您甚至不需要这种场景的泛型类型,因为Java会做同样的事情,就像处理“T”一样。唯一的优势是,您可以定义getter和Setter,并且不需要一直对对象进行类型转换。(因为Java正在为你这样做)(这被称为类型删除)