使用反射在 Java 中获取泛型参数的类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1901164/
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-08-12 23:40:56  来源:igfitidea点击:

Get type of a generic parameter in Java with reflection

javagenericsreflection

提问by cimnine

Is it possible to get the type of a generic parameter?

是否可以获取泛型参数的类型?

An example:

一个例子:

public final class Voodoo {
    public static void chill(List<?> aListWithTypeSpiderMan) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = ???;
    }

    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>());
    }
}

采纳答案by DerMike

One construct, I once stumbled upon looked like

一个构造,我曾经偶然发现看起来像

Class<T> persistentClass = (Class<T>)
   ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];

So there seems to be some reflection-magic around that I unfortunetly don't fully understand... Sorry.

所以似乎有一些反射魔法,不幸的是我没有完全理解......对不起。

回答by bertolami

This is impossible because generics in Java are only considered at compile time. Thus, the Java generics are just some kind of pre-processor. However you can get the actual class of the members of the list.

这是不可能的,因为 Java 中的泛型只在编译时考虑。因此,Java 泛型只是某种预处理器。但是,您可以获得列表成员的实际类。

回答by cletus

No it isn't possible.

不,这是不可能的。

You can get a generic type of a field given a class is the only exception to that rule and even that's a bit of a hack.

你可以得到一个字段的泛型类型,因为类是该规则的唯一例外,即使这有点小技巧。

See Knowing type of generic in Javafor an example of that.

有关示例,请参阅了解 Java 中的泛型类型

回答by Michael Borgwardt

Nope, that is not possible. Due to downwards compatibility issues, Java's generics are based on type erasure, i.a. at runtime, all you have is a non-generic Listobject. There is some information about type parameters at runtime, but it resides in class definitions (i.e. you can ask "what generic type does this field's definition use?"), not in object instances.

不,那是不可能的。由于向下兼容性问题,Java 的泛型基于类型擦除,即在运行时,您所拥有的只是一个非泛型List对象。在运行时有一些关于类型参数的信息,但它驻留在类定义中(即你可以问“这个字段的定义使用什么泛型类型?”),而不是在对象实例中。

回答by Jared Russell

Because of type erasure the only way to know the type of the list would be to pass in the type as a parameter to the method:

由于类型擦除,知道列表类型的唯一方法是将类型作为参数传递给方法:

public class Main {

    public static void main(String[] args) {
        doStuff(new LinkedList<String>(), String.class);

    }

    public static <E> void doStuff(List<E> list, Class<E> clazz) {

    }

}

回答by Andrey Rodionov

Actually I got this to work. Consider the following snippet:

其实我得到了这个工作。考虑以下片段:

Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
     if( genericParameterTypes[i] instanceof ParameterizedType ) {
                Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"

     }
 }

I'm using jdk 1.6

我正在使用 jdk 1.6

回答by Snicolas

As pointed out by @bertolami, it's not possible to us a variable type and get its future value (the content of typeOfList variable).

正如@bertolami 所指出的,我们不可能使用变量类型并获取其未来值(typeOfList 变量的内容)。

Nevertheless, you can pass the class as parameter on it like this:

不过,您可以像这样将类作为参数传递给它:

public final class voodoo {
    public static void chill(List<T> aListWithTypeSpiderMan, Class<T> clazz) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = clazz;
    }

    public static void main(String... args) {
        chill(new List<SpiderMan>(), Spiderman.class );
    }
}

That's more or less what Google does when you have to pass a class variable to the constructor of ActivityInstrumentationTestCase2.

当您必须将类变量传递给ActivityInstrumentationTestCase2的构造函数时,这或多或少是 Google 所做的。

回答by Mark McKenna

I want to try to break down the answer from @DerMike to explain:

我想尝试分解@DerMike的答案来解释:

First, type erasure does not mean that the JDK eliminatestype information at runtime. It's a method for allowing compile-time type checking and runtime type compatibility to coexist in the same language. As this block of code implies, the JDK retains the erased type information--it's just not associated with checked casts and stuff.

首先,类型擦除并不意味着 JDK在运行时消除类型信息。它是一种允许编译时类型检查和运行时类型兼容性在同一语言中共存的方法。正如这块代码所暗示的那样,JDK 保留了已擦除的类型信息——它只是与已检查的强制转换和其他内容无关。

Second, this provides generic type information to a generic class exactly one level up the heirarchy from the concrete type being checked--i.e. an abstract parent class with generic type parameters can find the concrete types corresponding to its type parameters for a concrete implementation of itself that directlyinherits from it. If this class were non-abstract and instantiated, or the concrete implementation were two levels down, this wouldn't work (although a little bit of jimmying could make it apply to any predetermined number of levels beyond one, or up to the lowest class with X generic type parameters, et cetera).

其次,这为泛型类提供了与被检查的具体类型正好上一级层次结构的泛型类型信息——即,具有泛型类型参数的抽象父类可以找到与其类型参数相对应的具体类型,用于自身的具体实现即直接从它继承。如果这个类是非抽象的和实例化的,或者具体的实现低于两个级别,这将不起作用(尽管一点点的 jimming 可以使它适用于超过一个的任何预定数量的级别,或者直到最低级别带有 X 泛型类型参数等)。

Anyway, on to the explanation. Here's the code again, separated into lines for ease of reference:

无论如何,继续解释。再次是代码,为了便于参考,分成几行:

1# Class genericParameter0OfThisClass = 
2#     (Class)
3#         ((ParameterizedType)
4#             getClass()
5#                .getGenericSuperclass())
6#                    .getActualTypeArguments()[0];

Let 'us' be the abstract class with generic types that contains this code. Reading this roughly inside out:

让“我们”成为包含此代码的具有泛型类型的抽象类。大致从里到外阅读:

  • Line 4 gets the current concrete class' Class instance. This identifies our immediate descendant's concrete type.
  • Line 5 gets that class' supertype as a Type; this is us. Since we're a parametric type we can safely cast ourselves to ParameterizedType (line 3). The key is that when Java determines this Type object, it uses type information present in the child to associate type information with our type parameters in the new ParameterizedType instance. So now we can access concrete types for our generics.
  • Line 6 gets the array of types mapped into our generics, in order as declared in the class code. For this example we pull out the first parameter. This comes back as a Type.
  • Line 2 casts the final Type returned to a Class. This is safe because we know what types our generic type parameters are able to take and can confirm that they will all be classes (I'm not sure how in Java one would go about getting a generic parameter that doesn't have a Class instance associated with it, actually).
  • 第 4 行获取当前具体类的 Class 实例。这标识了我们的直接后代的具体类型。
  • 第 5 行获取该类的超类型作为类型;这是我们。由于我们是参数类型,我们可以安全地将自己强制转换为 ParameterizedType(第 3 行)。关键是当 Java 确定这个 Type 对象时,它使用子对象中存在的类型信息将类型信息与我们在新 ParameterizedType 实例中的类型参数相关联。所以现在我们可以访问泛型的具体类型。
  • 第 6 行获取映射到泛型中的类型数组,按类代码中声明的顺序排列。对于这个例子,我们取出第一个参数。这作为类型返回。
  • 第 2 行将返回的最终类型转换为类。这是安全的,因为我们知道我们的泛型类型参数能够采用什么类型,并且可以确认它们都是类(我不确定在 Java 中如何获取没有 Class 实例的泛型参数实际上与之相关)。

...and that's pretty much it. So we push type info from our own concrete implementation back into ourselves, and use it to access a class handle. we could double up getGenericSuperclass() and go two levels, or eliminate getGenericSuperclass() and get values for ourselves as a concrete type (caveat: I haven't tested these scenarios, they haven't come up for me yet).

……差不多就是这样。所以我们将类型信息从我们自己的具体实现推回我们自己,并使用它来访问类句柄。我们可以将 getGenericSuperclass() 加倍并进入两个级别,或者消除 getGenericSuperclass() 并为我们自己获取作为具体类型的值(警告:我还没有测试过这些场景,它们还没有出现在我身上)。

It gets tricky if your concrete children are be an arbitrary number of hops away, or if you're concrete and not final, and especially tricky if you expect any of your (variably deep) children to have their own generics. But you can usually design around those considerations, so this gets you most of the way.

如果您的具体子代距离任意数量的跃点,或者如果您是具体的而不是最终的,这将变得棘手,如果您期望您的任何(不同深度的)子代拥有自己的泛型,则尤其棘手。但是您通常可以围绕这些考虑因素进行设计,因此这可以帮助您完成大部分工作。

Hope this helped someone! I recognize this post is ancient. I'll probably snip this explanation and keep it for other questions.

希望这对某人有所帮助!我承认这篇文章很古老。我可能会剪掉这个解释并将其保留用于其他问题。

回答by Yann-Ga?l Guéhéneuc

There is a solution actually, by applying the "anonymous class" trickand the ideas from the Super Type Tokens:

实际上有一个解决方案,通过应用“匿名类”技巧和来自Super Type Tokens的想法:

public final class Voodoo {
    public static void chill(final List<?> aListWithSomeType) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        System.out.println(aListWithSomeType.getClass().getGenericSuperclass());
        System.out.println(((ParameterizedType) aListWithSomeType
            .getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0]);
    }
    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>() {});
    }
}
class SpiderMan {
}

The trick lies in the creation of an anonymous class, new ArrayList<SpiderMan>() {}, in the place of the original (simple) new ArrayList<SpiderMan>(). The use of an anoymous class (if possible) ensures that the compiler retains information about the type argument SpiderMangiven to the type parameter List<?>. Voilà !

诀窍在于创造一个的匿名类new ArrayList<SpiderMan>() {}在原有的(简单)的地方new ArrayList<SpiderMan>()。使用匿名类(如果可能)可确保编译器保留有关SpiderMan提供给类型参数的类型参数的信息List<?>。瞧!

回答by Anton Pogonets

You cannot get a generic parameter from a variable. But you can from a method or field declaration:

您无法从变量中获取泛型参数。但是您可以从方法或字段声明:

Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();