如何为Java类字段生成准确的泛型表达式?
我试图在运行时对泛型进行推理。有几个很棒的库可以做到这一点(例如,gentyref、ClassMate和Guava)。然而,它们的用法有点让我摸不着头脑
具体来说,我想提取一个表达式,它与子类上下文中的特定字段相匹配
下面是一个使用gentyref的示例:
import com.googlecode.gentyref.GenericTypeReflector;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
public class ExtractArguments {
public static class Thing<T> {
public T thing;
}
public static class NumberThing<N extends Number> extends Thing<N> { }
public static class IntegerThing extends NumberThing<Integer> { }
public static void main(final String... args) throws Exception {
final Field thing = Thing.class.getField("thing");
// naive type without context
Class<?> thingClass = thing.getType(); // Object
System.out.println("thing class = " + thingClass);
Type thingType = thing.getGenericType(); // T
System.out.println("thing type = " + thingType);
System.out.println();
// exact types without adding wildcard
Type exactThingType = GenericTypeReflector.getExactFieldType(thing, Thing.class);
System.out.println("exact thing type = " + exactThingType);
Type exactNumberType = GenericTypeReflector.getExactFieldType(thing, NumberThing.class);
System.out.println("exact number type = " + exactNumberType);
Type exactIntegerType = GenericTypeReflector.getExactFieldType(thing, IntegerThing.class);
System.out.println("exact integer type = " + exactIntegerType);
System.out.println();
// exact type with wildcard
final Type wildThingType = GenericTypeReflector.addWildcardParameters(Thing.class);
final Type betterThingType = GenericTypeReflector.getExactFieldType(thing, wildThingType);
System.out.println("better thing type = " + betterThingType);
final Type wildNumberType = GenericTypeReflector.addWildcardParameters(NumberThing.class);
final Type betterNumberType = GenericTypeReflector.getExactFieldType(thing, wildNumberType);
System.out.println("better number type = " + betterNumberType);
final Type wildIntegerType = GenericTypeReflector.addWildcardParameters(IntegerThing.class);
final Type betterIntegerType = GenericTypeReflector.getExactFieldType(thing, wildIntegerType);
System.out.println("better integer type = " + betterIntegerType);
System.out.println();
System.out.println("desired thing type = T");
System.out.println("desired number thing type = N extends Number");
System.out.println("desired integer thing type = Integer");
}
}
以下是输出:
thing class = class java.lang.Object
thing type = T
exact thing type = class java.lang.Object
exact number type = class java.lang.Object
exact integer type = class java.lang.Integer
better thing type = capture of ?
better number type = capture of ?
better integer type = class java.lang.Integer
desired thing type = T
desired number thing type = N extends Number
desired integer thing type = Integer
我知道betterThingType
Type
对象(a gentyref-specific implementation)比这里的toString()
显示的更复杂。但我猜我需要再次用非通配符getExactFieldType
调用Type
来获得我想要的
我的主要要求是,我需要一个表达式,它可以成为代码生成的源文件的一部分,该源文件可以成功编译,或者至少只需很少的修改就可以编译。我愿意使用最适合这份工作的图书馆
# 1 楼答案
要获取此类信息,必须确定是否已向泛型类型参数提供了实际类型(例如
Integer
)。如果没有,您将需要获取类型参数名,正如您所需的类中已知的那样,以及任何边界事实证明这相当复杂。但首先,让我们回顾一下我们将在解决方案中使用的一些反射技术和方法
首先,^{}'s ^{} method 返回所需的
Type
信息。这里,Type
可以是一个简单的Class
,如果实际类作为类型提供,例如Integer thing;
,或者它可以是一个TypeVariable
,表示在Thing
中定义的泛型类型参数,例如T thing;
如果是泛型,那么我们需要了解以下内容:
Field
的原始类开始,extends
子句中提供了什么类型的参数。这些类型参数本身可能是Integer
之类的实际类型,也可能是它们自己的类的泛型类型参数。更复杂的是,这些类型参数的名称可能不同,并且它们的声明顺序可能与在超类中不同。可以通过调用^{extends
子句数据,它返回一个Type
,可以是简单的Class
,例如Object
,也可以是ParameterizedType
,例如Thing<N>
或NumberThing<Integer>
李>TypeVariable
数组TypeVariable
中,可以提取名称,例如T
,以及边界,作为Type
对象的数组,例如Number
用于N extends Number
李>对于泛型类型参数,我们需要跟踪哪些子类类型参数与原始泛型类型参数匹配,直到我们到达原始的
Class
,我们在其中报告具有任何边界的泛型类型参数,或者到达实际的Class
对象,我们在其中报告类这是一个基于你的课程的程序,可以报告你想要的信息
它必须创建一个
Stack
个Class
的类,从原始类一直到声明该字段的类。然后它弹出类,沿着类层次结构走。它会在当前类中查找与上一个类中的类型参数匹配的类型参数,并记录所有类型参数名称的更改以及当前类提供的新类型参数的新位置。例如,从Thing
到NumberThing
时T
变成N extends Number
。当类型参数是实际类时,循环迭代停止,例如Integer
,或者如果我们已经到达原始类,在这种情况下,我们报告类型参数名称和任何边界,例如N extends Number
我还包括了两个额外的类
Superclass
和Subclass
,其中Subclass
反转Superclass
中声明的泛型类型参数的顺序,以提供额外的测试。我还包括了SpecificIntegerThing
(非泛型),作为测试用例,以便迭代在IntegerThing
停止,在到达堆栈中的SpecificIntegerThing
之前报告Integer
getFieldTypeInformation
方法处理堆栈getTypeVariableString
方法有助于生成类型参数名和任何边界这会影响输出: