夯实Spring系列|第二十一章:Spring 泛型处理(Generic Resolution)
文章目录
本章说明
本章主要是介绍 Spring 中泛型的一些设计细节,以及一些关键 API 的使用。
1.项目环境
- jdk 1.8
- spring 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
- 本章模块:generic
2.Java 泛型基础
泛型类型
- 泛型类型是在类型上参数化的泛型类或者接口
泛型使用场景
- 编译时强类型检查
- 避免类型强转
- 实现通用算法
泛型类型擦写
泛型被引入到 Java 语言中,以便在编译是提供更严格的类型检查并支持泛型编程。类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。
为了实现泛型,编译器将类型擦除应用于:
- 将泛型类型中的所有类型参数替换为其边界,如果类型参数是无边界的,则将其替换为
Object
。因此,生产的字节码只包含普通类、接口和方法 - 必要时插入类型转换以保持类型安全
- 生成桥接方法时以保留扩展泛型类型中的多态性
2.1 举例说明
集合场景
public interface Collection<E> extends Iterable<E> {
...
boolean addAll(Collection<? extends E> c);
...
<? extends E>
可以保证参数的边界,E 或者 E 的子类,同时也确保的了多态性- E 表示插入元素类型,做一个类型的约束
2.2 代码示例
public class GenericDemo {
public static void main(String[] args) {
// Java 7 Diamond 语法
Collection<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
// 编译时错误
// list.add(1);
// 通过下面的方式可以欺骗编译器
// 泛型擦写
Collection temp = list;
// 编译通过
temp.add(1);
System.out.println(list);
}
}
3.Java 5 类型接口
Java 5 类型接口 - java.lang.reflect.Type
派生类或接口 | 说明 |
---|---|
java.lang.Class | Java 类 API,如 java.lang.String |
java.lang.reflect.GenericArrayType | 泛型数组类型 |
java.lang.reflect.ParameterizedType | 泛型参数类型 |
java.lang.reflect.TypeVariable | 泛型类型变量,Collection<E> 中的 E |
java.lang.reflect.WildcardType | 泛型通配类型 |
Java 泛型反射 API
类型 | API |
---|---|
泛型信息(Generics Info) | java.lang.Class#getGenericInfo |
泛型参数(Parameters) | java.lang.reflect.ParameterizedType |
泛型父类(Super Classes) | java.lang.Class#getGenericSuperclass |
泛型接口(Interfaces) | java.lang.Class#getGenericInterfaces |
泛型声明(Generics Declaration) | java.lang.reflect.GenericDeclaration |
API 使用示例
public class GenericAPIDemo {
public static void main(String[] args) {
// 原生类型 primitive types :int long float ...
Class intClass = int.class;
// 数组类型 array types:int[],Object[]
Class objectArrayClass = Object[].class;
// 原始类型 raw types:java.lang.String
Class rawClass = String.class;
System.out.printf("原生类型:[%s]\n数组类型:[%s]\n原始类型:[%s]\n", intClass, objectArrayClass, rawClass);
// 泛型参数类型 parameterized type
ParameterizedType parameterizedType = (ParameterizedType)ArrayList.class.getGenericSuperclass();
System.out.println(parameterizedType.toString());
Type[] typeVariables = parameterizedType.getActualTypeArguments();
Stream.of(typeVariables).map(TypeVariable.class::cast).forEach(System.out::println);
}
}
执行结果:
原生类型:[int]
数组类型:[class [Ljava.lang.Object;]
原始类型:[class java.lang.String]
java.util.AbstractList<E>
E
4.Spring 泛型类型辅助类
- 核心 API - org.springframework.core.GenericTypeResolver - Spring 2.5.2+
- 处理类型(Type)相关方法
- org.springframework.core.GenericTypeResolver#resolveReturnType
- org.springframework.core.GenericTypeResolver#resolveType(java.lang.reflect.Type, java.lang.Class<?>)
- 处理泛型参数类型(ParameterizedType)相关方法
- org.springframework.core.GenericTypeResolver#resolveReturnTypeArgument
- org.springframework.core.GenericTypeResolver#resolveTypeArgument
- org.springframework.core.GenericTypeResolver#resolveTypeArguments
- 处理泛型类型变量(TypeVariable)相关方法
- getTypeVariableMap
- 处理类型(Type)相关方法
API 操作示例
public class GenericTypeResolverDemo {
public static void main(String[] args) throws NoSuchMethodException {
Class clazz = GenericTypeResolverDemo.class;
System.out.println("======1=======");
displayReturnTypeGenericInfo(clazz, List.class, "getString", null);
// String 是 Comparable 接口的具体化
// public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
System.out.println("======2=======");
displayReturnTypeGenericInfo(clazz, Comparable.class, "getString", null);
System.out.println("======3=======");
displayReturnTypeGenericInfo(clazz, List.class, "getUsers", null);
System.out.println("======4=======");
displayReturnTypeGenericInfo(clazz, List.class, "getStringList", null);
System.out.println("======5=======");
displayReturnTypeGenericInfo(clazz, List.class, "getStringList2", null);
// TypeVariable
Map<TypeVariable, Type> typeVariableMap = GenericTypeResolver.getTypeVariableMap(StringList.class);
System.out.println(typeVariableMap);
}
public String getString() {
return "Hello,World";
}
public <E> List<E> getUsers() {
return Collections.emptyList();
}
/**
* 泛型参数的具体化
*
* @return
*/
public List<String> getStringList() {
return Collections.emptyList();
}
/**
* 泛型参数的具体化
*
* @return
*/
public StringList getStringList2() {
return null;
}
/**
* 打印返回类型泛型信息
*
* @param containingClass 所在的类
* @param genericIfc 泛型接口
* @param methodName 方法名称
* @param arguments 参数
* @throws NoSuchMethodException
*/
private static void displayReturnTypeGenericInfo(Class<?> containingClass, Class<?> genericIfc, String methodName, Class... arguments) throws NoSuchMethodException {
Method method = containingClass.getMethod(methodName, arguments);
Class<?> returnType = GenericTypeResolver.resolveReturnType(method, containingClass);
// 常规类作为方法返回值类型
System.out.println("返回值的类型:" + returnType);
// 常规类型不具备泛型参数类型
Class<?> returnTypeArgument = GenericTypeResolver.resolveReturnTypeArgument(method, genericIfc);
System.out.println("返回参数泛型类型:" + returnTypeArgument);
}
}
执行结果
======1=======
返回值的类型:class java.lang.String
返回参数泛型类型:null
======2=======
返回值的类型:class java.lang.String
返回参数泛型类型:class java.lang.String
======3=======
返回值的类型:interface java.util.List
返回参数泛型类型:null
Disconnected from the target VM, address: '127.0.0.1:51313', transport: 'socket'
======4=======
返回值的类型:interface java.util.List
返回参数泛型类型:class java.lang.String
======5=======
返回值的类型:class com.huajie.thinking.in.spring.generic.StringList
返回参数泛型类型:class java.lang.String
{T=class java.lang.String, E=class java.lang.String, E=class java.lang.String, E=class java.lang.String, E=class java.lang.String, E=class java.lang.String}
通过结果我们可以得到以下结论:
- 方法返回参数泛型类型必须是具体类型,才能通过 resolveReturnTypeArgument 取到相关信息
- 比如
List<String>
- 或者
class StringList extends ArrayList<String>
- 或者
public final class String implements Comparable<String>
5.Spring 泛型集合类型辅助类
核心 API - GenericCollectionTypeResolver - 该类Spring5.0 之后就被移除了,我们这里就不展开了,替换实现 ResolvableType,下面会进行讨论。
6.Spring 方法参数封装 - MethodParameter
核心 API - org.springframework.core.MethodParameter
- 起始版本 Spring 2.0+
- 元信息
- 关联的方法 - Method
- 关联的构造器 - Constructor
- 构造器或方法参数索引 - parameterIndex
- 构造器或方法参数类型 - parameterType
- 构造器或方法参数泛型类型 - genericParameterType
- 构造器或方法参数名称 - parameterName
- 所在的类 - containingClass
7.Spring 4.2 泛型优化实现 - ResolvableType
核心 API - org.springframework.core.ResolvableType
- 起始版本 Spring 4.0+
- 扮演角色:GenericTypeResolver 和 GenericCollectionTypeResolver 替代者
- 工厂方法:for* 方法
- 转换方法:as* 方法
- 处理方法:resolve* 方法
Java Doc 中的示例
private HashMap<Integer, List<String>> myMap;
public void example() throws NoSuchFieldException {
ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap"));
t.getSuperType(); // AbstractMap<Integer, List<String>>
t.asMap(); // Map<Integer, List<String>>
t.getGeneric(0).resolve(); // Integer
t.getGeneric(1).resolve(); // List
t.getGeneric(1); // List<String>
t.resolveGeneric(1, 0); // String
}
使用上个例子中的 StringList 来操作
public class ResolvableTypeDemo {
public static void main(String[] args) {
// 工厂创建
ResolvableType resolvableType = ResolvableType.forClass(StringList.class);
resolvableType.getSuperType();// ArrayList
resolvableType.getSuperType().getSuperType();// AbstractList
System.out.println(resolvableType.asCollection().resolve()); // 获取 Raw 类型
System.out.println(resolvableType.asCollection().resolveGeneric(0));// 获取泛型参数类型
}
}
测试结果:
interface java.util.Collection
class java.lang.String
8.ResolvableType 的局限性
局限一:ResolvableType 无法处理泛型擦写
局限二:ResolvableType 无法处理非具体化的 ParameterizedType
9.面试
9.1 Java 泛型擦写发生在编译时还是运行时?
运行时
9.2 Java 5 Type 类型的派生类或者接口有哪些?
第 3 小节 Java 5 类型接口
10.参考
- 极客时间-小马哥《小马哥讲Spring核心编程思想》