本章主要是介绍 Spring 中泛型的一些设计细节,以及一些关键 API 的使用。
泛型类型
泛型使用场景
泛型类型擦写
泛型被引入到 Java 语言中,以便在编译是提供更严格的类型检查并支持泛型编程。类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。
为了实现泛型,编译器将类型擦除应用于:
Object
。因此,生产的字节码只包含普通类、接口和方法集合场景
public interface Collection<E> extends Iterable<E> {
...
boolean addAll(Collection<? extends E> c);
...
extends E>
可以保证参数的边界,E 或者 E 的子类,同时也确保的了多态性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);
}
}
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 |
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
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, 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}
通过结果我们可以得到以下结论:
List
class StringList extends ArrayList
public final class String implements Comparable
核心 API - GenericCollectionTypeResolver - 该类Spring5.0 之后就被移除了,我们这里就不展开了,替换实现 ResolvableType,下面会进行讨论。
核心 API - org.springframework.core.MethodParameter
核心 API - org.springframework.core.ResolvableType
Java Doc 中的示例
private HashMap<Integer, List<String>> myMap;
public void example() throws NoSuchFieldException {
ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap"));
t.getSuperType(); // AbstractMap>
t.asMap(); // Map>
t.getGeneric(0).resolve(); // Integer
t.getGeneric(1).resolve(); // List
t.getGeneric(1); // List
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
局限一:ResolvableType 无法处理泛型擦写
局限二:ResolvableType 无法处理非具体化的 ParameterizedType
运行时
第 3 小节 Java 5 类型接口