【问题标题】:Java Generics: Accessing Generic Type at runtimeJava 泛型:在运行时访问泛型类型
【发布时间】:2012-03-03 18:41:23
【问题描述】:

我希望在运行时访问已声明字段的泛型类型。我以前的印象是,由于 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,因为他的帖子让我找到了这个解决方案,即使它没有直接回答我的问题。

【问题讨论】:

  • 我应该将此编辑移到我自己问题的答案中吗?试图找出将答案授予谁。

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


【解决方案1】:

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

反映泛型?

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

泛型和类型擦除的背景

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

  • 如果没有泛型支持的至少一些指示符(这里是所谓的菱形运算符 &lt;&gt;),就不可能有简写形式,
  • 无法在运行时检查泛型类型,因为它们必须使用 Type Erasure 实现。

进一步阅读

【讨论】:

  • 好的,那么 Guice 是如何找出合适的 Provider 的,就像我给出的例子一样?
  • 基本上,如果您在代码中指定了具体类型参数,那么 Guice 可以通过反射看到该方法声明的参数。
  • @Anony-Mousse:抱歉,您添加评论时我仍在输入此内容。我确实指出了罗伯逊使用这些的文章。 (我通常会以一小段时间写出答案,以防止在问题中输入任何内容被关闭,或者我不小心关闭了一个窗口,因为我认为如果他们看到一个已经在路上,它可以避免多个人输入非常相似的答案) .感谢您指出这一点。
  • 这个答案与问题有什么关系? OP 没有要求提供指向 java 教程的链接
  • @Haylem:感谢您提供的链接,非常有帮助。
【解决方案2】:

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

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

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

看看ParameterizedType的Java API。

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

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

【讨论】:

  • 接下来我打算去看看 Guice 的源码,只是想先问问这里有知识的人。谢谢指点。
  • 不要比较泛型和模板,暗示这两种语言特性有任何重要的共同点是一种误导。 (对于理解它们所需的心智模型,它们肯定没有任何共同点。)
  • 它们的工作方式可能完全不同(模板可以用于各种其他用途)。但它们通常服务于非常相似的需求,特别是对于类型专业化。为该提醒 +1。
【解决方案3】:

大约一年前我用过这个。可以帮到你

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; }
    };
}

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-27
      • 1970-01-01
      相关资源
      最近更新 更多