【问题标题】:Java generics - obtaining actual type of generic parameterJava 泛型 - 获取泛型参数的实际类型
【发布时间】:2013-11-08 23:55:48
【问题描述】:

我已经阅读了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接口的类型定义。

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

【问题讨论】:

    标签: java generics


    【解决方案1】:

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

    例如,如果您这样做:

    class IntegerList extends ArrayList<Integer> {}
    
    List<Integer> l = new IntegerList();
    

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

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

    表示一个泛型类型T。您可以使用此类来获取类的泛型类型。 > 例如,要获取Collection&lt;Foo&gt; 的泛型类型,您可以使用:

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

    这是因为这里创建的匿名类已经编译了它的类型参数为Collection&lt;Foo&gt;的信息。

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

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

    【讨论】:

      【解决方案2】:

      javadoc 会告诉你你在做什么。

      Class#getGenericSuperclass() 状态

      返回表示实体的直接超类的类型 (类、接口、原始类型或 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 类定义中使用的实际类型参数。

      【讨论】:

        【解决方案3】:

        您描述的方法仅在由于继承而设置通用类型时才有效,因为它在编译时是已知的:

         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 正在为您做到这一点) (称为类型擦除)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-09-13
          • 1970-01-01
          • 2017-06-07
          • 1970-01-01
          相关资源
          最近更新 更多