得先申明下,这篇文章应该不会有借鉴价值,我也只能是斗胆总结下。
为什么呢,因为这是我目前为止遇到问题最大的一部分,绕过去绕过来,可能是自己对泛型、类型学习都还不够,理解得很吃力很吃力。
代码部分来自博客:https://www.jianshu.com/p/73bb429b9a01
我实在懒得打注释了,解释就用文章的方式吧。
在开始介绍TypeParmeterResolver之前,得了解一下Type的知识:
Type是所有类型的父接口,它有四个子接口和一个实现类。
下面来看这些子接口和子类所代表的类型。
- Class 比较常见,它表示的是原始类型。Class 类的对象表示NM 中的一个类或接口,
每个Java 类在NM 里都表现为一个Class 对象。在程序中可以通过“类名.class ”、“对
象.getClass()”或是“Class.forName(‘类名’)”等方式获取class对象。数组也被映射成
Class 对象,所有元素类型相同且维数相同的数组都共享同一个Class 对象。 - ParameterizedType 表示的是参数化类型,例如List<String> 、Map<Integer,String>、
Service<User>这种带有泛型的类型。
ParameterizedType 接口中常用的方法有三个,分别是:- Type getRawType ()一一返回参数化类型中的原始类型,例如List<String > 的原始类
型为List 。 - Type[] getActualTypeArguments ()一一获取参数化类型的类型变量或是实际类型列
表,例如Map<Integer, String> 的实际泛型列表Integer 和String 。需要注意的是,
该列表的元素类型都是Type ,也就是说,可能存在多层嵌套的情况。 - Type getOwnerType ()一一返回是类型所属的类型,例如存在A类,其中定义了
内部类InnerA<I> ,则InnerA<I> 所属的类型为A<T>,如果是顶层类型则返回null 。
这种关系比较常见的示例是Map<K,V>接口与Map .Entry<K,V >接口, Map<K,V>
接口是Map.Entry<K,V >接口的所有者。
- Type getRawType ()一一返回参数化类型中的原始类型,例如List<String > 的原始类
- TypeVariable 表示的是类型变量,它用来反映在JVM编译该泛型前的信息。例如List
它用来反映在JVM编译该泛型前的信息。例如List中的T就是类型变量,它在编译时需被转换为一个具体的类型后才能正常使用。
该接口中常用的方法有三个,分别是:- Type[] getBounds ()一一获取类型变量的上边界,如果未明确声明上边界则默认为
Object 。例如class Test<K extends Person > 中K 的上界就是Person 。 - D getGenericDeclaration()一一获取声明该类型变量的原始类型,例如class Test<K extends Person>中的原始类型是Test 。
- String getName()一一获取在源码中定义时的名字,上例中为K 。
- Type[] getBounds ()一一获取类型变量的上边界,如果未明确声明上边界则默认为
- GenericArrayType 表示的是数组类型且组成元素是ParameterizedType 或TypeVariable .
例如List<String>[]或T[] 。该接口只有Type getGenericComponentType () 一个方法,它返回数组的组成元素。
WildcardType 表示的是通配符泛型,例如? extends Number 和? super Integer 。
WildcardType 接口有两个方法,分别是:
Type[] getUpperBounds ()一一-返回泛型变量的上界。
Type[] getLowerBounds()一-返回泛型变量的下界。
上面这些就是我很不熟的部分,所以给整个学习过程带来了挺大的麻烦。
那么咱回到TypeParameterResolver的介绍,他是一个工具类,提供了一系列静态方法来解析指定类中的字段、方法返回值或方法参数的类型。
也就是说,它露给了我们三个静态方法,分别对应解析字段类型、方法参数、方法返回值的类型解析。这三个方法里面内容差不多,通过指定类型的方式去调用resolverType方法,然后resolverType方法根据传过来的字段,再调用后边的方法。比如resolveFieldType:
/**
* 获取指定字段的实际类型,将泛型转换为实际类型
*
* @param field 字段
* @param srcType 运行时字段所属对象类型
* @return 如果field在声明的时候有泛型定义,这些泛型会被解析会运行时的实际类型
*/
public static Type resolveFieldType(Field field, Type srcType) {
// 获取字段的类型
Type fieldType = field.getGenericType();
// 获取声明字段的类
Class<?> declaringClass = field.getDeclaringClass();
// 执行解析操作
return resolveType(fieldType, srcType, declaringClass);
}
其中参数srcType指的是用于获取泛型实际类型的类/实际运行时的类/传参进来的类型所在的类,这是我看了很多博客后的总结。。反正也算理解那个意思了吧。
然后咱来到resolveType:
/**
* 将指定类型中的泛型定义转换为运行时的实际类型定义
*
* @param type 可能包含泛型定义的类型
* @param srcType 运行期间该类型所属的实例对象
* @param declaringClass 声明了type定义的类
* @return 指定类型处理完泛型定义后的类型
*/
private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
if (type instanceof TypeVariable) {
// 解析类型参数,比如: 泛型参数表示具体的某一独立的类型
return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
} else if (type instanceof ParameterizedType) {
// 解析参数化的泛型,比如:List<T> list;
return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
} else if (type instanceof GenericArrayType) {
// 解析泛型数组,比如: List<N>[] listArray;
return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
} else {
// 普通类型(Class)直接返回
return type;
}
}
resolveType的思路就是根据传参的type来确定接下来通过哪个方法来进行类型解析。
resolveTypeVar
我们进入第一个方法:resolveTypeVar,这个方法是用来解析TypeVariable的,也就是泛型,具体实现如下:
/**
* 解析指定泛型变量的类型
*
* @param typeVar 泛型变量
* @param srcType 用于获取泛型变量实际类型的类
* @param declaringClass 实际声明该类型的类的类型
*/
private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
Type result = null;
Class<?> clazz = null;
// step1: 处理srcType,移除泛型定义,获取对应的Class类型
if (srcType instanceof Class) {
clazz = (Class<?>) srcType;
} else if (srcType instanceof ParameterizedType) {
// 泛型参数取声明泛型的类型
ParameterizedType parameterizedType = (ParameterizedType) srcType;
clazz = (Class<?>) parameterizedType.getRawType();
} else {
// 理论上讲不会出现其他的类型场景
throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
}
// step2: 获取泛型定义的实际类型
// case1: 当前类就是声明了泛型的类
if (clazz == declaringClass) {
// 当前类就是声明了泛型的类,则泛型一定未被指定具体类型,获取泛型变量类型上限
Type[] bounds = typeVar.getBounds();
if (bounds.length > 0) {
return bounds[0];
}
// 没有指定泛型变量上限,那就是Object。
return Object.class;
}
// case2: 尝试从父类中获取泛型变量的实际类型
// 运行期间泛型所属的对象和声明泛型定义的不是同一个
// 获取其直接父类类型,尝试从父类中找到泛型定义
Type superclass = clazz.getGenericSuperclass();
// 递归处理父类,直到找到该泛型对应的实际类型,或者null值。
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
if (result != null) {
return result;
}
// case3: 无法通过父类获取泛型变量的实际类型,则通过接口定义获取泛型变量对应的实际类型
// 获取类实现的所有接口,尝试从接口中获取泛型变量的定义
Type[] superInterfaces = clazz.getGenericInterfaces();
for (Type superInterface : superInterfaces) {
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
if (result != null) {
return result;
}
}
// 在接口中也未找到泛型变量的实际类型,返回Object类型。
return Object.class;
}
这个方法具体实现注释里面太明白了,我这里理下思路:
- 首先是参数,第一个参数typeVar是指我们要处理的泛型对象,第二个参数srcType和之前的一样,传参进来那个地方的类,第三个参数指的是字段定义地方的类。
- 然后判断传参进来的类是不是个class对象,也就是前面介绍的TYPE接口下面的Class。是的话那么ok,那么要处理的就是它了,于是直接把type实列化成一个类。如果他不是class对象,而是嵌套了ParameterizedType类型,比如List,那么我们把前面这个List拿出来,就是后边要处理的了。
- 然后就是类型的处理了,如果上一步拿下来的要处理的类正好是声明字段的类,那没办法了,里面的泛型咱肯定是处理不了的了,就往上找找吧,是在不行返回一个object。
- 如果上一步不满足,那么找到实际类型还有希望,于是尝试从父类中去找,这里调用scanSuperTypes实现去父类中找的操作
- 如果在父类中还找不到,再尝试去接口中找找。实在不行就返回Object。
scanSuperTypes
那么上面第4步中出现的scanSuperTypes咱也得分析一下:
private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
Type result = null;
/*
* 判断处理的父类superclass是否为参数化类型,如果不是则代表declaringClass和superclass的基本Class
* 类型不是同一个类。
*/
if (superclass instanceof ParameterizedType) {
//如果为ParameterizedType,则获取它基本类型
ParameterizedType parentAsType = (ParameterizedType) superclass;
Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
if (declaringClass == parentAsClass) {
//如果declaringClass和parentAsClass表示同一类型,则通过typeVar在declaringClass的泛型形参的index获取其在supperClass中定义的类型实参
Type[] typeArgs = parentAsType.getActualTypeArguments();
TypeVariable<?>[] declaredTypeVars = declaringClass.getTypeParameters();
for (int i = 0; i < declaredTypeVars.length; i++) {
//循环判断当前处理的类型是否属于所属的类型描述符中的变量
if (declaredTypeVars[i] == typeVar) {
/*
*如果supperClass中定义的类型形参还是类型变量则取srcType中的类型形参的定义
* 如果srcType中的类型形参还是类型变量则不处理。
*/
if (typeArgs[i] instanceof TypeVariable) {
//其子类中的所有泛型描述符
TypeVariable<?>[] typeParams = clazz.getTypeParameters();
for (int j = 0; j < typeParams.length; j++) {
if (typeParams[j] == typeArgs[i]) {
//判断是否为ParameterizedType,则去实际代表的类型
if (srcType instanceof ParameterizedType) {
result = ((ParameterizedType) srcType).getActualTypeArguments()[j];
}
break;
}
}
} else {
//如果不是TypeVariable,直接取对应的类型
result = typeArgs[i];
}
}
}
} else if (declaringClass.isAssignableFrom(parentAsClass)) {
//通过判断superclass是否是declaringClass的子类(由于java类可以实现多个接口),进行递归解析
result = resolveTypeVar(typeVar, parentAsType, declaringClass);
}
} else if (superclass instanceof Class) {
//如果superclass为Class类型,通过判断superclass是否是declaringClass的子类(由于java类可以实现多个接口),进行递归解析
if (declaringClass.isAssignableFrom((Class<?>) superclass)) {
result = resolveTypeVar(typeVar, superclass, declaringClass);
}
}
return result;
}
同样的,注释写的太明白了,我自己理一遍思路:
-
- 首先是参数,第一、二、三个参数和上一步是一样的,分别是第一个参数typeVar是指我们要处理的泛型对象,第二个参数srcType和之前的一样,传参进来那个地方的类,第三个参数指的是字段定义地方的类。
第四个参数是从上一步的srcType分析出的待解析的类,第五个参数是带解析的类的父类(父接口)
- 首先是参数,第一、二、三个参数和上一步是一样的,分别是第一个参数typeVar是指我们要处理的泛型对象,第二个参数srcType和之前的一样,传参进来那个地方的类,第三个参数指的是字段定义地方的类。
-
- 首先判断第五个参数superclass是参数化类型,那么就取出它的原始类型typeArgs,比如List我们就拿出List。 如果这个typeArgs就是初始定义目标字段的类,那么就对头了。接下来拿出typeArgs的泛型参数比如{T},再拿出初始定义类的参数比如{k},判断这个k是不是我们要找的目标字段,是的话判断T是不是泛型了,然后就循环当前类clazz的参数列表,这里是看泛型在这里被参数化成了什么类型,如果当前类的某个参数类型和typeArgs里的参数类型相同,那就解析成功了。
-
- 如果superclass不是参数化类型,那就再往父类上递归找呗。
到这里resolveTypeVar就结束了,然后是resolveParameterizedType:
private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass) {
//获取泛型的基本类型
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
//获取泛型中的类型实参
Type[] typeArgs = parameterizedType.getActualTypeArguments();
//递归处理其类型实参
Type[] args = new Type[typeArgs.length];
for (int i = 0; i < typeArgs.length; i++) {
//判断对应参数的类型,分别进行递归处理
if (typeArgs[i] instanceof TypeVariable) {
//解析TypeVariable类型
args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
} else if (typeArgs[i] instanceof ParameterizedType) {
//解析ParameterizedType类型
args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
} else if (typeArgs[i] instanceof WildcardType) {
//是解析WildcardType类型(其类型实参是通配符表达式)
args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
} else {
//普通Class类型,直接返回
args[i] = typeArgs[i];
}
}
//返回自定以类型
return new ParameterizedTypeImpl(rawType, null, args);
}
resolveGenericArrayType和上面这个差不多,我也不再写了。。
到这里都还是昏昏的,过程大概有了个了解,但是和实列还是对不上,等后边学习到使用这个工具类部分的时候再理解吧。
个人博客:qlumen.cn