众所周知,java中泛型是 “编译时擦除” 的,但并不总是会被擦出。例如spring4中的泛型依赖注入就是通过动态获取泛型参数,对依赖进行匹配的。
Class类 对泛型参数的获取提供了两个API
return | method | desc |
---|---|---|
Type | getGenericSuperclass | 获取直接父类的(带泛型参数)的类型 |
Type[] | getGenericInterfaces | 获取实现接口的(带泛型参数)的类型 |
通过API可以确定的一点是:Java中要想获取泛型参数,只能获取父类或父接口的,而不能直接获取自己的。
何时会被擦?
定义测试类
class TestGeneric<T>{
}
该类编译后的字节码
public class org.java.springTest.TestGeneric<T extends java.lang.Object> extends java.lang.Object
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#9 = Utf8 <T:Ljava/lang/Object;>Ljava/lang/Object;
#14 = Utf8 java/lang/Object
Signature: #9 // <T:Ljava/lang/Object;>Ljava/lang/Object;
SourceFile: "TestGeneric.java"
可以看到 倒数第二行的注释 <T:Ljava/lang/Object;>
T被编译后是Object类型。这是正常的,因为TestGeneric类的不同实例,泛型参数都可以不同。而java并没有为每一个实例保留其指定的泛型类型,也没有提供任何API。
- 一个证明泛型擦除的例子
List<Integer> list = new ArrayList<Integer>();
Method m = list.getClass().getMethod("add",Object.class);
m.invoke(list, "abcd");
System.out.println(list.get(0)); //输出 abcd
何时不会被擦除?
定义测试类
interface IBase<T1,T2>{
}
class Base<T1,T2> { }
class Child extends Base<Double,Integer> implements IBase<String,Long>{
public void show() {
System.out.println(this.getClass().getGenericSuperclass());
}
}
编译后的字节码
class Child extends Base<java.lang.Double, java.lang.Integer> implements IBase<java.lang.String, java.lang.Long>
Constant pool:
LBase<Ljava/lang/Double;Ljava/lang/Integer;>;LIBase<Ljava/lang/String;Ljava/lang/Long;>;
Signature: #10 // LBase<Ljava/lang/Double;Ljava/lang/Integer;>;LIBase<Ljava/lang/String;Ljava/lang/Long;>;
SourceFile: "Base.java"
可以看出,在子类继承父类实现接口并指定了泛型参数后,编译器会保留我们的泛型参数。
如何得到泛型参数?
- 一个获取泛型参数的例子
//获得声明的父类泛型参数
ParameterizedType ts = (ParameterizedType)Child.class.getGenericSuperclass();
Stream.of(ts.getActualTypeArguments()).forEach(System.out::println);
//获得声明的父接口泛型参数
Type [] type2 = Child.class.getGenericInterfaces();
Arrays.stream(type2).forEach(
(tt)->
Arrays.stream(((ParameterizedType)tt).getActualTypeArguments()).forEach(System.out::println)
);
得到泛型参数有什么用?
一个典型的例子就是Spring4的泛型依赖注入,Spring4 通过 获取泛型参数 来匹配 有依赖的类。Spring4不在本章的讲解范畴,可以查阅本人在知乎提的问题 spring4的泛型依赖注入是什么原理?