有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java中泛型参数类的反射?

想象一下以下场景:

class MyClass extends OtherClass<String>{

   String myName;
   //Whatever

}

class OtherClass<T> {

   T myfield;

}

我正在使用反射分析MyClass,特别是(MyClass.class).getDeclaredFields(),在本例中,我将使用字段的getType()获得以下字段(和类型):

myName --> String
myField --> T

我想得到T的实际类型,这在运行时是已知的,因为extends表示法中有显式的“String”,如何获得myField的非遗传类型

编辑已解决:

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


共 (6) 个答案

  1. # 1 楼答案

    这只能通过反射实现,因为您显式地使用了String,否则由于类型擦除,此信息将丢失

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

    围绕here开展大型工作。它被称为“Gafters Gadget”模式。它被jackson和谷歌图书馆如guava使用

    /** *引用泛型类型。 * *@作者crazybob@google.com(李国宝) */

    ^{pr1}$

    }

  3. # 3 楼答案

    我找到了一个很好的解释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.

    简言之:

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

    代码:

    您无法在此处看到T

    class MyClass<T> { T myField; }
    

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

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

    在这里,您可以告诉fooField的类型和类型参数。 参见{}和{}的{}方法

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

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

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

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

    所以你得到了一个Type树。但这有点离题了。享受:)

  4. # 4 楼答案

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

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

  5. # 5 楼答案

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

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

    他们通常负担不起提供一切的费用;他们必须保留原始的程序文本

    因此,反射对象无法获得有关代码的所有其他事实

    解决这一问题的方法是在语言之外使用工具,该工具可以提供有关代码的任意信息。这种工具称为Program Transformation Systems (PTS)

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

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

  6. # 6 楼答案

    由于Type Erasure的原因,无法直接获取实际类型。但是,您可以使用以下方法:

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

    protected abstract class<T> getClazz();
    

    然后在MyClass中,实现以下方法:

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

    然后可以调用getClazz()来获取类