Java Generics:在运行时访问Generic Type

时间:2012-03-03 18:41:23

标签: java generics guice type-erasure annotation-processing

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

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

public class Injectable{

    @Inject
    private Provider<SomeType> someTypeProvider;

}

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

此外,了解如何通过Java 6 Annotation Processor 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,因为他的帖子引导我解决这个问题,即使它没有直接回答我的问题。

4 个答案:

答案 0 :(得分:14)

在Java中运行时通常不知道泛型,因为它们是使用Type Erasure实现的。

反映泛型?

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

泛型和类型擦除的背景

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

  • 没有至少一些支持仿制药的指标(这里就是所谓的钻石操作员 <>),
  • 是不可能的。
  • 无法在运行时检查泛型类型,因为它们必须使用 Type Erasure 来实现。

进一步阅读

答案 1 :(得分:4)

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

Guice在多个层面上做这些事情。特别突出的是type literals

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

查看ParameterizedType的Java API。

因此,虽然Java Generics在级别上通过“类型擦除”实现是正确的,但这并不完全保持在Type级别。不幸的是,处理类型比类更棘手。此外,这也意味着Java无法以与C ++相同的方式优化泛型,特别是对于原始类型。 Type设计为ArrayList<Integer>包含对象,并且在可能的情况下不由本地ArrayList<?>数组支持。

但请注意,这与自己跟踪这些事情非常接近。说,非常天真(它不适用于嵌套泛型),您可以使用具有字段int[]的类扩展ArrayList<T>,然后您将能够在运行时找到此信息。 (尽管如此,TypeLiteral可能是更好的选择,而不是类!)另外,JRE实际上不能确保列表保持一致。就像您可以将Class<T> contentClass投射到无类型ArrayList<Integer>并添加ArrayList对象一样。

答案 2 :(得分:2)

我大约一年前使用过它。它可以帮助你

Type typeOfSrc = type(YourClass.class, clazz);

// type(C, A1,...,An) => C<A1,...,An>
static ParameterizedType type(final Class raw, final Type... args)
{
    return new ParameterizedType()
    {
        public Type getRawType(){ return raw; }

        public Type[] getActualTypeArguments(){ return args; }

        public Type getOwnerType(){ return null; }
    };
}

这允许您在运行时访问Generic类型。基本上你在这里做的是将通用信息存储在另一个类中,并使用该类在运行时检索该信息。

答案 3 :(得分:0)

我相信guice使用TypeLiteral将通用信息封装在一个单独的对象中。