java Java中泛型参数类的反射?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11164756/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 04:02:46  来源:igfitidea点击:

Reflection for Class of generic parameter in Java?

javagenericsreflectiontypes

提问by Sam Stern

Imagine the following scenario:

想象以下场景:

class MyClass extends OtherClass<String>{

   String myName;
   //Whatever

}

class OtherClass<T> {

   T myfield;

}

And I am analyzing MyClass using reflection specifically (MyClass.class).getDeclaredFields(), in this case I will get the following fields (and Types, using getType() of the Field):

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

myName --> String
myField --> T

I want to get the actual Type for T, which is known at runtime due to the explicit "String" in the extends notation, how do I go about getting the non-genetic type of myField?

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

EDIT RESOLVED:

编辑解决:

Seems like the answer is "you can't". For those who may look at this question later I'd recommend using Hymanson (I was trying to do this to generate JSON) and annotating your classes and fields in such a way so that Hymanson is aware of the inheritance hierarchy and can automatically do what the correct answer below suggested.

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

回答by Jeffrey

This can be achieved with reflection only because you explicitly used String, otherwise this information would've been lost due to type erasure.

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

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String>
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String>

回答by Ondra ?i?ka

I found a nice explanation here:

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

When runtime inspecting a parameterizable type itself, like java.util.List, there is no way of knowing what type is has been parameterized to. This makes sense since the type can be parameterized to all kinds of types in the same application. But, when you inspect the method or field that declares the use of a parameterized type, you can see at runtime what type the paramerizable type was parameterized to.

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

In short:

简而言之:

You cannot see on a type itself what type it is parameterized to a runtime, but you can see it in fields and methods where it is used and parameterized.

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

In code:

在代码中:

You can't see There:

你看不到T这里:

class MyClass<T> { T myField; }

You can see the "T" here:

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

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

Here you would be able to tell the type and type parameters of fooField. See getGeneric*()methods of Classand Method.

在这里,您将能够告诉fooField. 请参阅和 的getGeneric*()方法。ClassMethod

By the way, I often see this (shortened):

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

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

This is not correct, because getActualTypeArguments()may, and often will, return TypeVariableinstead of class - that's when the generic is <? extends SomeClass>instead of just <SomeClass>. It can go deeper, imagine:

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

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

So you get a tree of Types. But that's a bit off-topic. Enjoy :)

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

回答by Ira Baxter

This is a classic example of why reflection is not a great idea.

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

What you can get from a program by reflection are only those facts that the compiler people for the language chose to make available.

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

And they generally can't afford to make everything available; they'd sort of have to keep the raw program text around.

而且他们通常负担不起让所有东西都可用;他们必须保留原始程序文本。

All the other facts about your code are thus not available to the reflectee.

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

The cure for this is to step outsidethe language and use a tool that canprovide any arbitrary bit of information about the code. Such tools are called Program Transformation Systems (PTS).

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

A PTS parses the source code and builds an AST the represents it. A good PTW will build an AST that holds essentially everything about the code (operators, operands, punctuation, comments) so that it can be inspected. Normally a PTS will record the line/column position of language tokens so even layout information is available; extreme PTS will record the whitespace in the gaps between tokens or at least know how to read the original text file when necessary if asked about it. This AST is in essence the equivalent of the full text I said would be necessary, but in a more convenient form to process.

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

(PTSs have one other very nice property: they can modifythe AST and regenerate code for the modified program. But that is above and beyond reflection so I won't comment further on this aspect).

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

回答by one

greate work around here. it is call the "Gafters Gadget" pattern. it is used by Hymanson and google libraries such as guava.

这里工作。它被称为“Gafters Gadget”模式。Hymanson 和 google 库(例如 guava)使用它。

/** * References a generic type. * * @author [email protected] (Bob Lee) */

/** * 引用泛型类型。* * @author [email protected] (Bob Lee) */


public abstract class 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(); }

}

}

回答by Jochen

Generic types are notknown at runtime. Only the compiler knows about them, checks that your program is typed correctly, and then removes them.

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

In your particular case, calling MyClass.class.getGenericSuperclass()might give you the information you need, because for some strange reason, the concrete types used when inheriting are kept in the class descriptor.

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

回答by Chun

There is no direct way to get the actual type because of the Type Erasure. However you can use the following way:

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

in your OtherClass<T>, write the following abstract method:

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

protected abstract class<T> getClazz();

Then in MyClass, you implement the method:

然后在 中MyClass,您实现该方法:

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

then you can call getClazz()to get the class.

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