在工作中,我们时常需要使用泛型,最主要目的其实还是在于对公共逻辑的提取,以最大限度减少重复代码带来的复杂性和难以维护性。在一文读懂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
以上就是关于泛型的反射解析全部内容,如果有任何问题,欢迎留言区评论。