在工作中,我们时常需要使用泛型,最主要目的其实还是在于对公共逻辑的提取,以最大限度减少重复代码带来的复杂性和难以维护性。在一文读懂java泛型机制中对泛型做了基本概念的介绍,以及基础的使用。今天这篇文章主要是通过反射的方式获取泛型信息。
1. 反射
在java类型编译后的类信息都存放在Class对象中,在Class中包含了字段Field, 方法Method等定义原始信息,例如:
public class ClassTest { public static void main(String[] args) { Class<String> clazz = String.class; System.out.println(clazz.getName()); String[] arr = {}; Class<? extends String[]> arrClazz = arr.getClass(); System.out.println(arrClazz.getName()); } }
执行以上代码,可以得到以下输出结果:
java.lang.String [Ljava.lang.String;
Class对象本身也是泛型类型,我们看到获取到的Class对象带有泛型标识,同样通过泛型限制了Class中存储的部分数据类型。获取class对象有两种方式:
- 根据类型调用.class可以获取到当前类型的Class对象
- 根据实例对象调用.getClass()方法,获取Class对象
当我们获取到了Class对象之后,就可以通过Class对象操作Field, Method等信息,并获取字段Field的值和执行Method方法,这里就不做演示,有兴趣可以搜集下反射相关资料。
2. 反射泛型
在一文读懂java泛型机制 一文中,谈到泛型包含了泛型类,泛型方法,泛型接口三种类型,因此我们就这三种类型泛型分别展开讨论,看如何通过泛型的方式获取实际的类型。
在反射中,最顶层的类型为Type, Type下又分为不同的实现类型,具体的结构体系如下:
每个类型的使用,会在后面的讲解中涉及到并讲解,因此每个类型的单独使用就不做介绍。
2.1 泛型类
定义一个泛型类型,代码如下:
public class GenericType<T>{ private T value; public GenericType(T value) { this.value = value; } public GenericType() {} public T getValue() { return this.value; } public void setValue(T t) { this.value = t; } }
我们对以上类型做测试,通过反射的方式查看获取到的泛型内容:
public class GenericTypeTest { public static void main(String[] args) throws NoSuchFieldException { Class<GenericType> genericTypeClass = GenericType.class; System.out.println(genericTypeClass); Type type = genericTypeClass.getGenericSuperclass(); System.out.println(type); // Object GenericType<Integer> genericType = new GenericType<>(); Class<? extends GenericType> aClass = genericType.getClass(); type = aClass.getGenericSuperclass(); System.out.println(type); // Object } }
对应输出结果如下:
class java.lang.Object class java.lang.Object
在上面的测试中,我们使用了getGenericSuperclass()获取泛型的超类,GenericType本身并没有集成或者实现其他类型,因此我们获取到的始终都是Object类型,这个时候我们就不能通过这种方式获取泛型信息,而是要使用getTypeParameters()方法获取类型上的泛型信息,测试代码如下:
// 获取参数化类型 TypeVariable<? extends Class<? extends GenericType>>[] typeParameters = aClass.getTypeParameters(); for (int i = 0; i < typeParameters.length; i++) { TypeVariable<? extends Class<? extends GenericType>> typeParameter = typeParameters[i]; System.out.println("typeName: " + typeParameter.getTypeName()); System.out.println("name: " + typeParameter.getName()); System.out.println("bounds: " + typeParameter.getBounds()); System.out.println("genericDeclaration: " + typeParameter.getGenericDeclaration()); System.out.println("annotatedBounds: " + typeParameter.getAnnotatedBounds()); }
执行以上代码,我们得到如下输出结果:
typeName: T name: T bounds: [Ljava.lang.reflect.Type;@1b6d3586 genericDeclaration: class com.java.demo.generic.GenericType annotatedBounds: [Ljava.lang.reflect.AnnotatedType;@74a14482
因此这里我们遇到了第一个TypeVariable类型的实现,这个类型中记录了泛型定义的原始信息,包括泛型的边界、泛型标识符、泛型声明的类型等。
这里对TypeVariable类型中的主要方法做一个晓得总结:
- getTypeName()获取泛型标识的名称
- getName()等同于getTypeName()方法
- getBounds()获取泛型边界信息,如果没有明确声明上边界,默认为Object. 因为下边界最终也是Object
- getGenericDeclaration()获取泛型声明类型
- getAnnotatedBounds()该方法从1.8版本时候加入,用于获取注解类型的上界,如果没有明确声明上边界,默认为长度为0的数组
这里也许会有个疑问,在第二GenericType使用的时候,明确的声明了泛型类型为Type, 为什么最终还是获取不到Integer的泛型参数类型?
这主要是因为泛型擦除,当我们实际在使用的时候指定泛型,在类型上使用泛型其实并不是一个新的类型,而是通过checkcast指令判断对应的值是否与泛型类型保持一致,因此以上的示例中通过泛型获取到的信息始终是Object类型的主要原因
继续探讨泛型类,我们为GenericType增加一个实现类,并明确指定泛型类型, 具体代码如下:
public class SubGenericType extends GenericType<Integer> { }
对SubGenericType进行测试,测试代码如下:
public class SubGenericTypeTest { public static void main(String[] args) throws NoSuchFieldException { Class<SubGenericType> typeClass = SubGenericType.class; Type type = typeClass.getGenericSuperclass(); System.out.println("isParameterizeType:" + (type instanceof ParameterizedType)); System.out.println("type:" + type); if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; System.out.println("rawType: " + parameterizedType.getRawType()); Type[] actualParameters = parameterizedType.getActualTypeArguments(); for (int i = 0; i < actualParameters.length; i++) { Type actualType = actualParameters[i]; System.out.println("generic actualType:" + actualType); } System.out.println("getRawType:" + parameterizedType.getRawType()); System.out.println("getOwnerType:" + parameterizedType.getOwnerType()); Field value = ((Class<GenericType>) parameterizedType.getRawType()).getDeclaredField("value"); System.out.println("Field.getType:" + value.getType()); System.out.println("Field.getGenericType:" + value.getGenericType().getClass()); } SubGenericType subGenericType = new SubGenericType(); subGenericType.setValue(23); // setValue(Integer) Integer value = subGenericType.getValue(); // Integer System.out.println("value:" + value); } }
以上代码执行结果输出如下:
isParameterizeType:true type:com.java.demo.generic.GenericType<java.lang.Integer> rawType: class com.java.demo.generic.GenericType generic actualType:class java.lang.Integer getRawType:class com.java.demo.generic.GenericType getOwnerType:null Field.getType:class java.lang.Object Field.getGenericType:class sun.reflect.generics.reflectiveObjects.TypeVariableImpl value:23
我们通过类继承并指定泛型具体类型,通过getGenericSuperClass()方法获取到了ParameterizeType的类型,ParameterizeType的方法介绍如下:
- getRawType()用于获取泛型定义的原始这类型,这里就是GenericType. 从ParameterizeType源码上来看,这里返回的一定是一个Class对象
- getOwnerType()从源码来看,getOwnerType()实际上等同于Class.getDeclaringClass(), 这里返回的是null
- getAcutalArguments()用于获取ParameterizeType中泛型的实际类型,在上面的例子中,我们获取到的是Integer类型
通过上面两个例子,我们基本上可以看到ParameterizeType和TypeVariable之间的一个区别:
- 当泛型被真正指定为具体的类型的时候,返回的是ParameterizeType类型,这个时候可以通过该类型获取到真正的泛型类型
- 当我们需要获取泛型定义、泛型边界信息时,则使用TypeVariable, 其中包含了详细的泛型信息
细心的小伙伴留意到了上面示例的最后几行代码,我们在定义的时候,实际上是没有重写父类GenericType中的方法,但是我们实际使用的时候,却能够直接调用setValue(Integer)这样的方法,我们反编译SubGenericeType类查看:
在反编译.class文件发现,也没有生成对应的桥接方法或者setValue(Integer)方法,那为什么可以直接调用setValue(Integer)方法呢?
我们反编译测试类的SubGenericTypeTest.class文件,对应的反编译信息如下:
我们从反编译中可以看出:
- setValue()的时候,最终还是调用的SubGenericType#setValue(Object)方法,这个方法在调用的时候并不会报错,因为Integer本身就是Object的子类,这个本身是没有问题的。
- getValue()也是使用的SubGenericeType#getValue(Object)方法,只是使用了checkcast类型转换的指令,以达到泛型类型验证的目的。
也许又有小伙伴有疑问,既然调用setValue(Object)方法,我可以设置setValue(“123”), 这个层面主要从编译器控制,保证传入的数据类型必须为Integer, 因此会导致编译错误。
2.2 泛型接口
泛型接口其实和泛型类型相似,他们定义和使用的方式很多也是相同的。我们定义以下泛型接口:
public interface GenericInterface<T> { T getVal(); void setVal(T val); }
并定义以下两个实现类:
public class GenericInterfaceImpl implements GenericInterface<Integer> { private Integer val; @Override public Integer getVal() { return null; } @Override public void setVal(Integer val) { } public static void main(String[] args) { GenericInterface<String> genericInterface = new GenericInterfaceImpl2<>(); } } class GenericInterfaceImpl2<T> implements GenericInterface<T> { private T val; @Override public T getVal() { return null; } @Override public void setVal(T val) { this.val = val; } }
以上两个实现类,和我们泛型类的定义相似,GenericInterfaceImpl2这个本身也是一个泛型,并且将泛型传递给了GenericInterface接口,而GenericInterfaceImpl给出了实际的泛型Integer. 这里我们就不着重讲,直接对他们进行测试,并获取泛型的实际类型。
则对应的测试类如下:
public class GenericInterfaceTest { public static void main(String[] args) { Class<GenericInterface> genericInterfaceClass = GenericInterface.class; System.out.println("GenericInterface genericSuperClass: " + genericInterfaceClass.getGenericSuperclass()); // null TypeVariable<Class<GenericInterface>>[] typeParameters = genericInterfaceClass.getTypeParameters(); for (int i = 0; i < typeParameters.length; i++) { TypeVariable<Class<GenericInterface>> typeParameter = typeParameters[i]; System.out.println("GenericInterface TypeVariable:" + typeParameter.getName()); // T } Class<GenericInterfaceImpl2> impl2Class = GenericInterfaceImpl2.class; Type[] genericSuperclass = impl2Class.getGenericInterfaces(); for (Type superclass : genericSuperclass) { System.out.println("GenericInterfaceImpl2 type: " + superclass.getClass()); // ParameterizeType if (superclass instanceof ParameterizedType) { ParameterizedType type = (ParameterizedType) superclass; Type[] actualTypeArguments = type.getActualTypeArguments(); Arrays.stream(actualTypeArguments) .forEach(argument -> { System.out.println("GenericInterfaceImpl2 actual type: " + argument.getTypeName()); System.out.println("GenericInterfaceImpl2 actual type class: " + argument.getClass()); }); } } Class<GenericInterfaceImpl> genericInterfaceClass1 = GenericInterfaceImpl.class; genericSuperclass = genericInterfaceClass1.getGenericInterfaces(); for (Type superclass : genericSuperclass) { System.out.println("GenericInterfaceImpl type: " + superclass.getClass()); // ParameterizeType if (superclass instanceof ParameterizedType) { ParameterizedType type = (ParameterizedType) superclass; Type[] actualTypeArguments = type.getActualTypeArguments(); Arrays.stream(actualTypeArguments) .forEach(argument -> { System.out.println("GenericInterfaceImpl actual type: " + argument.getTypeName()); System.out.println("GenericInterfaceImpl actual type class: " + argument.getClass()); }); } } } }
则对应的输出结果如下:
GenericInterface genericSuperClass: null GenericInterface TypeVariable:T GenericInterfaceImpl2 type: class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl GenericInterfaceImpl2 actual type: T GenericInterfaceImpl2 actual type class: class sun.reflect.generics.reflectiveObjects.TypeVariableImpl GenericInterfaceImpl type: class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl GenericInterfaceImpl actual type: java.lang.Integer GenericInterfaceImpl actual type class: class java.lang.Class
这里其实和泛型类展现保持一致,因此这里就不做过多的阐述。在上面接口定义中,我们定义了两个泛型方法,在GenericInterfaceImpl 中,因为明确指定了泛型的类型,因此在实现方法的时候,会将参数以及返回值替换成为具体的类型,这种方式本身是重写接口中的方法,并且会生成桥接方法,我们可以验证一下:
Method method = genericInterfaceClass1.getMethod("setVal", Object.class); System.out.println("isBridge: " + method.isBridge());
则对应输出结果如下:
isBridge: true
2.3 泛型方法
泛型方法主要包含了两种:
- 泛型信息记录在对应方法上
- 泛型类中的实例泛型方法
还是以上面泛型接口中的两个类作为实例,查看实例中的泛型方法。实例方法中的泛型方法,泛型定义会随着实力的不同实现而有所不同。首先我们提取公共的方法,用于对方法的泛型化参数进行提取和处理:
/** * 输出泛型方法 * * @param clazz 类型 * @param method 方法 * @param params 形参 */ public static final void printGenericMethod(Class<?> clazz, String method, Class... params) { try { System.out.println("-----------------" + clazz.getName() + "#" + method + "--------------------------"); Method targetMethod = clazz.getMethod(method, params); // 获取泛型参数 System.out.println("method parameter start ++++++++++++++++++++++++"); Type[] genericParameterTypes = targetMethod.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("type name: " + genericParameterType.getTypeName()); System.out.println("type class:" + genericParameterType.getClass().getName()); if (genericParameterType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericParameterType; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { if (actualTypeArgument instanceof WildcardType) { WildcardType wildcardType = (WildcardType) actualTypeArgument; Type[] lowerBounds = wildcardType.getLowerBounds(); if (Objects.nonNull(lowerBounds)) { for (Type lowerBound : lowerBounds) { System.out.println("wildcard type lower bound: " + lowerBound.getTypeName()); } } Type[] upperBounds = wildcardType.getUpperBounds(); if (Objects.nonNull(upperBounds)) { for (Type upperBound : upperBounds) { System.out.println("wildcard type upper bound: " + upperBound.getTypeName()); } } } else { System.out.println("ParameterizedType actual type: " + actualTypeArgument.getTypeName()); } } } else if (genericParameterType instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) genericParameterType; Type[] bounds = typeVariable.getBounds(); if (Objects.nonNull(bounds)) { for (Type bound : bounds) { System.out.println("TypeVariable bounds:" + bound.getTypeName()); } } } else { System.out.println("other type:" + genericParameterType.getClass().getName()); } } System.out.println("method parameter end ++++++++++++++++++++++++"); System.out.println("Method return type start+++++++++++++++++++"); Type genericReturnType = targetMethod.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericReturnType; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { if (actualTypeArgument instanceof WildcardType) { WildcardType wildcardType = (WildcardType) actualTypeArgument; Type[] lowerBounds = wildcardType.getLowerBounds(); if (Objects.nonNull(lowerBounds)) { for (Type lowerBound : lowerBounds) { System.out.println("wildcard return type lower bound: " + lowerBound.getTypeName()); } } Type[] upperBounds = wildcardType.getUpperBounds(); if (Objects.nonNull(upperBounds)) { for (Type upperBound : upperBounds) { System.out.println("wildcard return type upper bound: " + upperBound.getTypeName()); } } } else { System.out.println("ParameterizedType return actual type: " + actualTypeArgument.getTypeName()); } } } else if (genericReturnType instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) genericReturnType; Type[] bounds = typeVariable.getBounds(); if (Objects.nonNull(bounds)) { for (Type bound : bounds) { System.out.println("TypeVariable return bounds:" + bound.getTypeName()); } } } else { System.out.println("other type:" + genericReturnType.getClass().getName()); } System.out.println("Method return type end+++++++++++++++++++"); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } }
这样就能够方便我们对泛型化方法进行处理,测试代码如下:
public static void main(String[] args) throws NoSuchMethodException { Class<GenericInterface> genericInterfaceClass = GenericInterface.class; // 获取setVal方法 printGenericMethod(genericInterfaceClass, "getVal"); printGenericMethod(genericInterfaceClass, "setVal", Object.class); System.out.println("=========================================================="); Class<GenericInterfaceImpl2> impl2Class = GenericInterfaceImpl2.class; // 获取setVal方法 printGenericMethod(impl2Class, "getVal"); printGenericMethod(impl2Class, "setVal", Object.class); System.out.println("=========================================================="); Class<GenericInterfaceImpl> genericInterfaceClass1 = GenericInterfaceImpl.class; // 获取setVal方法 printGenericMethod(genericInterfaceClass1, "getVal"); printGenericMethod(genericInterfaceClass1, "setVal", Integer.class); }
输出结果如下:
-----------------com.java.demo.generic.GenericInterface#getVal-------------------------- method parameter start ++++++++++++++++++++++++ method parameter end ++++++++++++++++++++++++ Method return type start+++++++++++++++++++ TypeVariable return bounds:java.lang.Object Method return type end+++++++++++++++++++ -----------------com.java.demo.generic.GenericInterface#setVal-------------------------- method parameter start ++++++++++++++++++++++++ type name: T type class:sun.reflect.generics.reflectiveObjects.TypeVariableImpl TypeVariable bounds:java.lang.Object method parameter end ++++++++++++++++++++++++ Method return type start+++++++++++++++++++ other type:java.lang.Class Method return type end+++++++++++++++++++ ========================================================== -----------------com.java.demo.generic.GenericInterfaceImpl2#getVal-------------------------- method parameter start ++++++++++++++++++++++++ method parameter end ++++++++++++++++++++++++ Method return type start+++++++++++++++++++ TypeVariable return bounds:java.lang.Object Method return type end+++++++++++++++++++ -----------------com.java.demo.generic.GenericInterfaceImpl2#setVal-------------------------- method parameter start ++++++++++++++++++++++++ type name: T type class:sun.reflect.generics.reflectiveObjects.TypeVariableImpl TypeVariable bounds:java.lang.Object method parameter end ++++++++++++++++++++++++ Method return type start+++++++++++++++++++ other type:java.lang.Class Method return type end+++++++++++++++++++ ========================================================== -----------------com.java.demo.generic.GenericInterfaceImpl#getVal-------------------------- method parameter start ++++++++++++++++++++++++ method parameter end ++++++++++++++++++++++++ Method return type start+++++++++++++++++++ other type:java.lang.Class Method return type end+++++++++++++++++++ -----------------com.java.demo.generic.GenericInterfaceImpl#setVal-------------------------- method parameter start ++++++++++++++++++++++++ type name: java.lang.Integer type class:java.lang.Class other type:java.lang.Class method parameter end ++++++++++++++++++++++++ Method return type start+++++++++++++++++++ other type:java.lang.Class Method return type end+++++++++++++++++++
对于方法的解析,基本原则也是和泛型类保持一致,明确有参数返回的时候,返回的是ParameterizedType类型,返回的是一个泛型标识的时候,则采用的是TypeVariable对象。这两个类的使用在泛型类中已经介绍的很清楚了,这里就不介绍了。
接下来我们主要看下WildcardType类型的使用,我们在GenericInterfaceImpl2中定义如下方法:
public Class<? extends Integer> transfer(Class<? super Integer> clazz) { return null; }
这个方法本身没有实质性的意义,只是为了说明WildcardType的使用,
我们还是通过上面的方法,测试获取到的泛型信息:
printGenericMethod(impl2Class, "transfer", Class.class);
对应的输出结果如下:
-----------------com.java.demo.generic.GenericInterfaceImpl2#transfer-------------------------- method parameter start ++++++++++++++++++++++++ type name: java.lang.Class<? super java.lang.Integer> type class:sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl wildcard type lower bound: java.lang.Integer wildcard type upper bound: java.lang.Object method parameter end ++++++++++++++++++++++++ Method return type start+++++++++++++++++++ wildcard return type upper bound: java.lang.Integer Method return type end+++++++++++++++++++
首先输出的是泛型化参数,泛型化参数定义我们使用的是<? super Integer>, 那么能够传入Integer, Number, Object的对象,当我们在解析上边界的时候,实际上解析成了两个:
- 下边界为Integer
- 上边界为Object
这个和我们分析上边界时,保持是一致的。
在解析返回泛型化参数的时候,返回结果定义为上边界,因此能够传入Integer以及Integer子类(这里不太恰当,Integer本身定义为final, 不会产生子类, 所以这里只能返回Integer)。所以在解析为上边界的时候,组中被解析为Integer.
2.4 GenericArrayType
另外一个泛型类型为GenericArrayType, 这里主要描述的是泛型的数组类型,下面我们通过测试代码测试一下:
public class GenericArrayTypeTest { public static void main(String[] args) { Class<GenericArrayTypeTest> clazz = GenericArrayTypeTest.class; Method[] methods = clazz.getMethods(); for (Method method : methods) { if ("method".equals(method.getName())) { Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { if (genericParameterType instanceof GenericArrayType) { System.out.println("GenericArrayType ----> " + genericParameterType + "; genericComponentType ----> " + ((GenericArrayType) genericParameterType).getGenericComponentType()); } else { System.out.println("Not GenericArrayType ----> " + genericParameterType); } } } } } public static <T> void method(String[] strings, List<String> ls, List<String>[] lsa, T[] ts, List<T>[] tla, T[][] tts) { } }
通过上面的测试可以得到一下结论:
- 该类只是针对泛型数组,集合类型并不能解析为GenericArrayType
- 对于二位数组,最终解析的类型为T[]
Not GenericArrayType ----> class [Ljava.lang.String; Not GenericArrayType ----> java.util.List<java.lang.String> GenericArrayType ----> java.util.List<java.lang.String>[]; genericComponentType ----> java.util.List<java.lang.String> GenericArrayType ----> T[]; genericComponentType ----> T GenericArrayType ----> java.util.List<T>[]; genericComponentType ----> java.util.List<T> GenericArrayType ----> T[][]; genericComponentType ----> T[]
参考文章
https://www.cnblogs.com/throwable/p/12315988.html
以上就是关于泛型的反射解析全部内容,如果有任何问题,欢迎留言区评论。