概述
由于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>() {});其本质为:拿到类型Typepublic <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);}}}
