9

想象以下场景:

class MyClass extends OtherClass<String>{

   String myName;
   //Whatever

}

class OtherClass<T> {

   T myfield;

}

我正在专门使用反射分析 MyClass (MyClass.class).getDeclaredFields(),在这种情况下,我将获得以下字段(和类型,使用字段的 getType() ):

myName --> String
myField --> T

我想获取 T 的实际类型,由于扩展表示法中的显式“字符串”在运行时已知,我该如何获取 myField 的非遗传类型?

编辑解决:

似乎答案是“你不能”。对于那些以后可能会看到这个问题的人,我建议使用 Jackson(我试图这样做以生成 JSON)并以这样的方式注释您的类和字段,以便 Jackson 知道继承层次结构并可以自动执行什么下面建议的正确答案。

4

6 回答 6

26

这只能通过反射来实现,因为您明确使用了String,否则此信息将由于类型擦除而丢失。

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String>
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String>
于 2012-06-22T22:39:34.503 回答
4

我在这里找到了一个很好的解释:

在运行时检查可参数化类型本身时,例如 java.util.List,无法知道已参数化为什么类型。这是有道理的,因为可以将类型参数化为同一应用程序中的各种类型。但是,当您检查声明使用参数化类型的方法或字段时,您可以在运行时看到参数化类型被参数化为什么类型。

简而言之:

您无法在类型本身上看到它被参数化到运行时的类型,但您可以在使用和参数化它的字段和方法中看到它。

在代码中:

你看不到T这里:

class MyClass<T> { T myField; }

你可以在T这里看到“”:

class FooClass {
    MyClass<? extends Serializable> fooField;
}

在这里,您将能够分辨fooField. 参见和getGeneric*()的方法。ClassMethod

顺便说一句,我经常看到这个(缩短):

Class fieldArgClass = (Class) aType.getActualTypeArguments()[0];

这是不正确的,因为getActualTypeArguments()可能并且经常会返回TypeVariable而不是 class - 那是泛型<? extends SomeClass>而不是 just的时候<SomeClass>。它可以更深入,想象一下:

class FooClass {
    MyClass<? extends Map<String, List<? extends Serializable>>> fooField;
}

所以你得到一棵Types 的树。但这有点跑题了。享受 :)

于 2016-07-20T12:32:22.663 回答
1

这是一个典型的例子,说明为什么反射不是一个好主意。

您可以通过反射从程序中获得的只是该语言的编译器人员选择提供的那些事实。

而且他们通常负担不起提供所有东西。他们不得不保留原始程序文本。

因此,反映者无法获得有关您的代码的所有其他事实。

解决此问题的方法是跳出语言并使用可以提供有关代码的任意信息的工具。此类工具称为程序转换系统 (PTS)

PTS 解析源代码并构建代表它的 AST。一个好的 PTW 将构建一个 AST,它基本上包含有关代码的所有内容(运算符、操作数、标点符号、注释),以便可以对其进行检查。通常,PTS 将记录语言标记的行/列位置,因此可以获得甚至布局信息;极端 PTS 将记录标记之间的空白,或者至少知道如何在必要时读取原始文本文件(如果被问及)。这个 AST 本质上相当于我所说的需要的全文,但以更方便的形式处理。

(PTS 还有一个非常好的特性:它们可以修改AST 并为修改后的程序重新生成代码。但这超出了反射范围,因此我不会在这方面进一步评论)。

于 2016-07-20T13:02:31.357 回答
0

泛型类型在运行时是未知的只有编译器知道它们,检查你的程序是否输入正确,然后删​​除它们。

在您的特定情况下,调用MyClass.class.getGenericSuperclass()可能会为您提供所需的信息,因为出于某种奇怪的原因,继承时使用的具体类型保存在类描述符中。

于 2012-06-22T22:15:23.970 回答
0

伟大的工作在这里。它被称为“Gafters Gadget”模式。它被杰克逊和谷歌图书馆如番石榴使用。

/** * 引用一个泛型类型。* * @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<String> l1 = new TypeReference<ArrayList<String>>() {}.newInstance(); List l2 = new TypeReference<ArrayList>() {}.newInstance(); }

}

于 2018-09-09T08:22:31.457 回答
-5

由于Type Erasure,没有直接的方法来获取实际类型。但是,您可以使用以下方式:

在您的 中OtherClass<T>,编写以下抽象方法:

protected abstract class<T> getClazz();

然后在 中MyClass,实现方法:

@Override
protected Class<String> getClazz(){
    return String.class;
}

然后你可以打电话getClazz()来上课。

于 2012-06-22T22:14:32.803 回答