Java 泛型:在运行时访问泛型类型

我希望在运行时访问已声明字段的泛型类型。我以前的印象是,由于Java类型擦除,这是不可能的。但是,情况一定不是这样,因为一些众所周知的框架在运行时通过反射来利用泛型类型。

例如,Guice 将根据您提供的泛型类型实现提供程序:

public class Injectable{

    @Inject
    private Provider<SomeType> someTypeProvider;

}

如何通过反射 API 访问字段的“SomeType”泛型属性或任何此类类型/方法/等?

此外,了解如何通过 Java 6 注释处理器 API 访问这些泛型类型属性也会很有帮助。

谢谢。

编辑:

谢谢大家的精彩指点。我找到了一种方法来使用haylem的链接来做到这一点,特别是Prenkov的文章Java Reflection:Generics

这是我一直在寻找的答案:

/**
 * @author John Ericksen
 */
public class TypeReflectionExample {

    public class SomeType{}

    public class Injectable{
        @Inject  private Provider<SomeType> someTypeProvider;
    }

    public static void main(String[] args){

        try {
            Field providerField = Injectable.class.getDeclaredField("someTypeProvider");

            Type genericFieldType = providerField.getGenericType();

            if(genericFieldType instanceof ParameterizedType){
                ParameterizedType aType = (ParameterizedType) genericFieldType;
                Type[] fieldArgTypes = aType.getActualTypeArguments();
                for(Type fieldArgType : fieldArgTypes){
                    Class fieldArgClass = (Class) fieldArgType;
                    System.out.println("fieldArgClass = " + fieldArgClass);
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }   
}

结果:

fieldArgClass = class test.TypeReflectionExample$SomeType

方法,构造函数,超类扩展/实现等也可以这样做

我正在奖励haylem,因为他的帖子引导我找到了这个解决方案,即使它没有直接回答我的问题。


答案 1

的确,泛型在Java中通常不是在运行时已知的,因为它们是用类型擦除实现的。

反映泛型?

但是,您可以提取一些关于声明类型(而不是运行时对象类型)的有价值信息,如 Ian Roberston 的文章 Reflecting Generics 和 Prenkov 的文章 Java Reflection: Generics 中所述。

泛型和类型擦除的背景

在源 qnd 二进制级别保持向后兼容性的同时引入泛型,因此它们有一些限制,例如:

  • 不可能有一个简写形式,而没有至少一些泛型支持的指标(这里,所谓的菱形运算符),<>
  • 无法在运行时检查泛型类型,因为它们必须使用类型擦除来实现。

延伸阅读


答案 2

好吧,你为什么不看看guice是做什么的呢?来源是公开的。

Guice在多个层面上做这些事情。特别突出的是文字类型

这里的关键点是,虽然类型是使用类型擦除编译的(因此每种类型只有一个),但仍然存在多个对象,这些对象确实知道所使用的泛型。但是,代码是独立于此进行优化的(因为它是按类编译的,而不是按类型编译的)。Type

看看ParabellizedType的Java API。

因此,虽然Java泛型在级别上通过“类型擦除”实现是正确的,但这并不完全保持在一个级别上。不幸的是,处理类型比类要棘手得多。此外,这也意味着Java不能像C++那样优化泛型,特别是对于基元类型。根据设计,will 是一个包含对象,并且在可能的情况下不受本机数组的支持。TypeArrayList<Integer>ArrayList<?>int[]

但是,请注意,这非常接近于自己跟踪这些事情。说,非常天真(它不适用于嵌套泛型),你可以只用一个类进行扩展,它有一个字段,然后你将能够在运行时找到这些信息。(不过,TypeLiteral可能比这里的类更好选择!此外,JRE实际上不会确保列表保持一致。就像您可以将 a 转换为非类型化并添加对象一样。ArrayList<T>Class<T> contentClassArrayList<Integer>ArrayListString


推荐