Java Generics:在运行时访问Generic Type
我想在运行时访问声明字段的generics类型。 我之前的印象是,由于Java类型擦除,这是不可能的。 但是,情况并非如此,因为一些众所周知的框架在运行时通过reflection来利用generics类型。
例如,Guice将根据您提供的generics类型实现Provider:
public class Injectable{ @Inject private Provider someTypeProvider; }
如何通过reflectionAPI访问字段的“SomeType”generics属性或任何此类类型/方法/等?
此外,了解如何通过Java 6 Annotation Processor API访问这些generics类型属性也很有帮助。
谢谢。
编辑:
谢谢大家的指示。 我找到了一种方法,使用haylem的链接,特别是Prenkov的文章Java Reflection:Generics 。
这是我正在寻找的答案:
/** * @author John Ericksen */ public class TypeReflectionExample { public class SomeType{} public class Injectable{ @Inject private Provider 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
对于Methods,Constructors,Superclass扩展/实现等也可以这样做
我正在授予haylem,因为他的post引导我解决这个问题,即使它没有直接回答我的问题。
确实,在Java中运行时通常不知道generics,因为它们是使用Type Erasure实现的。
反映generics?
但是,您可以提取一些有关已声明类型的有价值信息(而不是运行时对象的类型),如Ian Roberston的文章Reflecting Generics和Prenkov的文章Java Reflection:Generics中所述 。
generics和类型擦除的背景
引入generics,同时保留源qnd二进制级别的向后兼容性,因此它们有一些限制,例如:
- 没有至少一些仿制药支持指标(这里是所谓的钻石操作员
<>
)的短手forms是不可能的, - 不可能在运行时检查generics类型,因为它们必须使用Type Erasure实现。
进一步阅读
- 来自Java教程 :
- 关于通用类型的部分
- 关于类型推断和通用类实例化的部分
- 从Java 语言规范 (JLS):
- Java SE 5的 类型,值和变量 的JLS部分
- Java SE 7的 类型,值和变量 的JLS部分
- 从好的StackOverflow问题:
- Java Raw Type和generics交互
- 其他:
- IBM开发人员系列: Java理论与实践:generics陷阱 (特别是“ 未采取的道路 , 使现有的类和擦除的影响”部分 )。
好吧,你为什么不看看guice的作用呢? 来源是公开的。
Guice在多个层面上完成这些工作。 一个特别突出的是类型文字 。
这里的关键点是虽然类型是使用类型擦除编译的 (因此每种类型只有一个类 ),但仍然存在多个Type
对象,它们确实知道所使用的generics。 但是,代码是独立于此优化的(因为它是按类编译的,而不是按类型编译)。
看看ParameterizedType
的Java API。
因此,虽然Java Generics在类级别上通过“类型擦除”实现是正确的,但这并不完全适用于Type
级别。 不幸的是,处理类型比类更棘手。 此外,这也意味着Java不能像C ++那样优化generics,特别是对于原始类型。 ArrayList
将设计为包含对象的ArrayList>
,并且在可能的情况下不由本机int[]
数组支持。
但请注意,这与自己跟踪这些事情非常接近。 说,非常天真(它不适用于嵌套generics),你可以用一个类扩展ArrayList
,它有一个字段Class
,然后你就可以在运行时找到这些信息。 (尽管如此,TypeLiteral可能是更好的选择,而不是类!)另外,JRE实际上不能确保列表保持一致。 就像您可以将ArrayList
转换为无类型的ArrayList
并添加String
对象一样。
我大约一年前用过这个。 它可以帮助你
Type typeOfSrc = type(YourClass.class, clazz); // type(C, A1,...,An) => C 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类型。 基本上你在这里做的是将通用信息存储在另一个类中,并使用该类在运行时检索该信息。
我相信guice使用TypeLiteral将通用信息封装在一个单独的对象中。