15

我正在寻找在运行时访问声明字段的泛型类型。我以前的印象是,由于 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 = 类 test.TypeReflectionExample$SomeType

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

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

4

4 回答 4

15

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

反映泛型?

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

泛型和类型擦除的背景

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

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

延伸阅读

于 2012-03-03T18:46:54.113 回答
4

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

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

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

看看ParameterizedType.

因此,虽然 Java 泛型在类级别上通过“类型擦除”实现是正确的,但这并不完全适用于一个Type级别。不幸的是,处理类型比类要复杂得多。另外,这也意味着 Java 不能像 C++ 那样优化泛型,特别是对于原始类型。遗嘱ArrayList<Integer>设计为ArrayList<?>包含对象,并且尽可能不受本机int[]数组的支持。

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

于 2012-03-03T18:58:14.327 回答
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; }
    };
}

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

于 2012-03-03T19:13:33.613 回答
0

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

于 2012-03-03T20:22:25.683 回答