Java中generics参数类的反映?
想象一下以下场景:
class MyClass extends OtherClass{ String myName; //Whatever } class OtherClass { T myfield; }
我正在使用特定的reflection分析MyClass (MyClass.class).getDeclaredFields()
,在这种情况下,我将获得以下字段(和类型,使用字段的getType()):
myName --> String myField --> T
我想获得T的实际类型,由于扩展符号中的显式“字符串”,它在运行时已知,如何获取myField的非遗传类型?
编辑决议:
似乎答案是“你不能”。 对于那些以后可能会看到这个问题的人,我建议使用Jackson(我试图这样做来生成JSON)并以这样的方式注释你的类和字段,以便jackson知道inheritance层次结构并且可以自动地做什么建议的正确答案如下。
这可以通过reflection来实现,因为你明确地使用了String
,否则这些信息会因类型擦除而丢失。
ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass Class> clazz = (Class>) t.getActualTypeArguments()[0]; // Class
我在这里找到了一个很好的解释
当运行时检查可参数化类型本身(如java.util.List)时,无法知道已参数化的类型。 这是有道理的,因为类型可以参数化为同一应用程序中的所有类型。 但是,当您检查声明使用参数化类型的方法或字段时,您可以在运行时看到参数化参数化类型的类型。
简而言之:
您无法在类型本身上看到它被参数化为运行时的类型,但您可以在使用和参数化的字段和方法中看到它。
在代码中:
你在这里看不到T
:
class MyClass { T myField; }
你可以在这里看到“ T
”:
class FooClass { MyClass extends Serializable> fooField; }
在这里,您可以告诉fooField
的类型和类型参数。 请参阅Class
和Method
getGeneric*()
方法。
顺便说一句,我经常看到这个(缩短):
Class fieldArgClass = (Class) aType.getActualTypeArguments()[0];
这是不正确的,因为getActualTypeArguments()
可能并且经常会返回TypeVariable
而不是class – 那时generics是 extends SomeClass>
extends SomeClass>
而不仅仅是
。 它可以更深入,想象:
class FooClass { MyClass extends Map>> fooField; }
所以你得到了一个Type
s树。 但这有点偏离主题。 请享用 :)
通用类型在运行时是未知的。 只有编译器知道它们,检查您的程序是否正确输入,然后删除它们。
在您的特定情况下,调用MyClass.class.getGenericSuperclass()
可能会为您提供所需的信息,因为由于某些奇怪的原因,inheritance时使用的具体类型保存在类描述符中。
这是为什么反思不是一个好主意的典型例子。
通过reflection从程序中获得的只是那些语言的编译人员选择提供的事实。
他们通常无法承担一切可用的费用; 他们必须保持原始程序文本。
因此,关于您的代码的所有其他事实都不可用于被拒绝者。
解决这个问题的方法是走出语言并使用可以提供任何有关代码的任意信息的工具。 这些工具称为程序转换系统(PTS) 。
PTS解析源代码并构建一个代表它的AST。 一个好的PTW将构建一个AST,它基本上包含有关代码(运算符,操作数,标点符号,注释)的所有内容,以便可以检查它。 通常,PTS将记录语言标记的行/列位置,因此甚至可以获得布局信息; 极端PTS会在令牌之间的间隙中记录空白,或者至少知道在需要时如何阅读原始文本文件。 这个AST本质上相当于我说的必要的全文,但是以更方便的forms处理。
(PTS还有另外一个非常好的属性:它们可以修改 AST并为修改后的程序重新生成代码。但这超出了reflection范围,所以我不会在这方面做进一步的评论)。
在这里工作。 它被称为“Gafters Gadget”模式。 它被jackson和谷歌图书馆如番石榴使用。
/ ** *引用generics类型。 * * @author crazybob@google.com(Bob Lee)* /
公共抽象类TypeReference {
private final Type type; private volatile Constructor> constructor; protected TypeReference() { Type superclass = getClass().getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0]; } /** * Instantiates a new instance of {@code T} using the default, no-arg * constructor. */ @SuppressWarnings("unchecked") public T newInstance() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { if (constructor == null) { Class> rawType = type instanceof Class> ? (Class>) type : (Class>) ((ParameterizedType) type).getRawType(); constructor = rawType.getConstructor(); } return (T) constructor.newInstance(); } /** * Gets the referenced type. */ public Type getType() { return this.type; } public static void main(String[] args) throws Exception { List
}
由于类型擦除,没有直接获得实际类型的方法。 但是,您可以使用以下方式:
在您的OtherClass
,编写以下抽象方法:
protected abstract class getClazz();
然后在MyClass
,实现该方法:
@Override protected Class getClazz(){ return String.class; }
然后你可以调用getClazz()
来获取类。