概述
由于Java泛型的引入,各种场景(虚拟机解析,反射等)下的方法调用都可能对原有的基础产生新的需求,如在泛型类中如何获取传入的参数化类型,因此,引入了诸如Signature
、LocalVariableTypeTable
等新的属性用于解决伴随泛型而来的参数识别问题。Signature
是其中最重要的一项属性,他的作用就是存储一个方法字节码
层次的特征签名,这个属性保存的参数类型并不是原生类型,而是包括了参数化(Parameterized Type
)类型的信息。
另外。从Signature
属性中,我们也可以得出结论,擦除法所谓的擦除:
方法中泛型在
字节码进行擦除,泛型信息保存在Signature
中。- 元数据(
类、属性、方法签名
)还是保存了泛型信息。 - 如果泛型带有:extends XXX或super XXX ,擦除后的类型为XXX类型。
反射获取元数据的泛型
- 获取属性上的泛型类型:
field.getGenericType(); 获取方法结构——形参的泛型类型:
ParameterizedType parameterType = (ParameterizedType)method.getGenericParameterTypes()[0];
parameterType.getActualTypeArguments()[0]获取方法结构——返回值的泛型类型:
method.getGenericReturnType();
ParameterizedType returnType = (ParameterizedType) method.getGenericReturnType();
returnType.getActualTypeArguments()[0]
示例
public class GenericType<T extends Number> {
private T data;
public <T extends Number> GenericType<T> apply(List<Long> list, Set<Double> set) {
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Class<?> clazz = GenericType.class;
//获取类的属性字段
Field[] declaredField = clazz.getDeclaredFields();
//暴力解除,可以访问私有变量
Field.setAccessible(declaredField, true);
System.out.println("属性名\t参数类型\t\t参数泛型类型");
for (Field field : declaredField) {
String name = field.getName();
Class<?> type = field.getType();
Type genericType = field.getGenericType();
System.out.println(name + "\t" + type + "\t" + genericType);
}
Method method = clazz.getDeclaredMethod("apply", new Class[]{List.class, Set.class});
Type[] types = method.getGenericParameterTypes();
System.out.println("方法形参的泛型类型\n");
for (Type type : types) {
ParameterizedType parameterType = (ParameterizedType) type;
System.out.println(parameterType.getActualTypeArguments()[0]); //获取第一个
}
System.out.println("方法返回值的泛型类型");
ParameterizedType returnType = (ParameterizedType) method.getGenericReturnType();
System.out.println("泛型类型: " + returnType.getActualTypeArguments()[0]);
System.out.println("RawType: " + returnType.getRawType());
}
获取实参泛型类型
只能通过子类来获取传入父类的泛型参数
Type type = this.getClass().getGenericSuperclass();
Type type = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
通过声明构造函数为protected,让外部类只能通过匿名类来创建,间接可以调用
Type type = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0] 并获取实际的泛型参数。
示例
public class GenericType<T extends Number> {
protected final Type type;
protected GenericType(){
System.out.println(this.getClass().getGenericSuperclass());
Type superClass = getClass().getGenericSuperclass();
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
this.type = type;
}
public <T extends Number> Collection<T> apply(List<T> list, Set<T> set) {
List<T> container = new ArrayList<>(list);
container.addAll(set);
return container;
}
public static void main(String[] args) throws NoSuchMethodException {
System.out.println("方法实参泛型类型");
GenericType<Integer> subType = new GenericType<Integer>() {
};
System.out.println(subType);
}
}
Fastjson#TypeReference
/**
* Constructs a new type literal. Derives represented class from type
* parameter.
*
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
* parameter in the anonymous class's type hierarchy so we can reconstitute it
* at runtime despite erasure.
*/
protected TypeReference(){
Type superClass = getClass().getGenericSuperclass();
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
Type cachedType = classTypeCache.get(type);
if (cachedType == null) {
classTypeCache.putIfAbsent(type, type);
cachedType = classTypeCache.get(type);
}
this.type = cachedType;
}
用法:
Map<String, String> map = JSON.parseObject("json", new TypeReference<Map<String, String>>() {
});
JSONObject json = new JSONObject();
GenericType gt = json.toJavaObject(new TypeReference<GenericType>() {
});
其本质为:拿到类型Type
public <T> T toJavaObject(TypeReference typeReference) {
Type type = typeReference != null ? typeReference.getType() : null;
return TypeUtils.cast(this, type, ParserConfig.getGlobalInstance());
}
递归获取所有泛型
public void show(Type type) {
System.out.println("当前泛型: " + type);
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType)type;
System.out.println("RawType: " + pt.getRawType());
Type[] ata = pt.getActualTypeArguments();
System.out.println("ActualType: " + Arrays.toString(ata));
for (Type t : ata) {
show(t);
}
}
}