获取 Java 中具有反射的泛型参数的类型

2022-08-31 07:08:37

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

例如:

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

答案 1

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

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

因此,周围似乎有一些反思的魔力,不幸的是,我并不完全理解......不好意思。


答案 2

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

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

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

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

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

设 “us” 是包含此代码的泛型类型的抽象类。从内到外粗略地阅读以下内容:

  • 第 4 行获取当前具体类的 Class 实例。这确定了我们直系后代的具体类型。
  • 第 5 行将该类的超类型作为类型获取;这就是我们。由于我们是参数化类型,因此我们可以安全地将自己转换为参数化类型(第 3 行)。关键是,当 Java 确定此 Type 对象时,它使用子对象中存在的类型信息将类型信息与新的 ParameterizedType 实例中的类型参数相关联。因此,现在我们可以访问泛型的具体类型。
  • 第 6 行获取映射到泛型中的类型数组,按类代码中声明的顺序排列。对于此示例,我们提取出第一个参数。这又回到了类型。
  • 第 2 行将最终的类型转换为类。这是安全的,因为我们知道我们的泛型类型参数能够采用什么类型,并且可以确认它们都是类(我不确定在Java中如何获取一个没有与之关联的Class实例的泛型参数, 实际上)。

...差不多就是这样。因此,我们将类型信息从我们自己的具体实现推送回我们自己,并使用它来访问类句柄。我们可以将getGenericSuperclass()加倍并达到两个级别,或者消除getGenericSuperclass()并为自己获取作为具体类型的值(注意:我还没有测试这些场景,它们还没有出现给我)。

如果你的具体孩子是任意数量的跳跃,或者如果你是具体的而不是最终的,那就变得棘手了,如果你期望你的任何一个(可变深度的)孩子有自己的泛型,那就特别棘手了。但是您通常可以围绕这些考虑因素进行设计,因此这可以让您获得大部分收益。

希望这对某人有所帮助!我承认这篇文章是古老的。我可能会剪掉这个解释,并保留它以解决其他问题。