1、首先了解什么是泛型擦除?
所谓的泛型擦除(Type Erasure),就是指在编译时,JVM 编译器会将所有的泛型信息都擦除掉,变成原始类型,一般是将泛型的类型参数替换成具体类型的上限或下限 (如果没有指定上界,则默认为Object)。
例如,看下列代码
static class BasicService<T> {
void save(T t) {
System.out.println("执行save方法,保存 " + t);
}
}
在运行时执行的代码就是这样的
static class BasicService<Object> {
void save(Object t) {
System.out.println("执行save方法,保存 " + t);
}
}
2、回归正题,在运行时能够拿到那些泛型相关信息呢?
结论:
1、当没有具体化参数类型时,只能够是被当做是 Object 或者 extends 上限。
2、当有具体化类型参数,在下面的情况下可以通过反射拿到。
-
继承泛型父类
-
实现泛型接口
-
成员变量(字段)
-
方法参数、返回值、异常声明
3、举例说明
1、当没有具体化时,如 BasicService<T> ,方法 save 中的参数 t 只能够使用 Object 中的方法。
当泛型 T 使用了 extends 类型上限,如下,那么泛型 T 就是 Person 类的子类,save 中的参数 t 就可以使用 Person 类中的方法了。
static class Person {
public void save() {
}
}
static class BasicService<T extends Person> {
void save(T t) {
System.out.println("执行save方法,保存 " + t);
}
}
2、具体化
2.1、继承泛型父类,如下在运行时是可以拿到 TeacherService 中的泛型类型的。
public class Teacher extends Person {
}
static class TeacherService extends BasicService<Teacher> {
}
2.2、实现泛型接口,和 2.1 类似
2.3、当泛型字段是成员变量时也可以获取,例如下代码中的 x 字段。
static class BasicService<T extends Person> {
void save(T t) {
System.out.println("执行save方法,保存 " + t);
}
List<String> x;
List<Random> y(List<Number> z,Set<Boolean> s) {
return null;
}
}
2.4、方法参数和返回值中的泛型类型也可以获取到,例如上面代码中的 y 方法。
特别说明,当泛型具体化时,但是不满足那四个条件也是不能够拿到泛型类型的,例如下代码中,是不能够拿到 stringList 的泛型类型的。
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
}
4、获取实现
4.1、继承泛型父类
Type type = TeacherService.class.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Type[] types = pt.getActualTypeArguments();
System.out.println("types = " + Arrays.toString(types));
}
//运行结果
types = [class com.zjj.learn.mls.s1.day12.Test12$Teacher]
Process finished with exit code 0
4.2、实现泛型接口就是用 getGenericInterfaces() 方法即可
4.3、成员变量
Field x = BasicService.class.getDeclaredField("x");
Type xType = x.getGenericType();
if (xType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) xType;
Type[] types = pt.getActualTypeArguments();
System.out.println("types = " + Arrays.toString(types));
}
// 运行结果
types = [class java.lang.String]
Process finished with exit code 0
4.4、方法参数和返回值中的泛型
Method y = BasicService.class.getDeclaredMethod("y", List.class, Set.class);
Type yReturnType = y.getGenericReturnType();
Parameter[] parameters = y.getParameters();
//获取返回值的类型
if (yReturnType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) yReturnType;
Type[] returnTypes = pt.getActualTypeArguments();
System.out.println("returnTypes = " + Arrays.toString(returnTypes));
}
//获取参数类型
for (Parameter parameter : parameters) {
Type parameterizedType = parameter.getParameterizedType();
if (parameterizedType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) parameterizedType;
Type[] yParamType = pt.getActualTypeArguments();
System.out.println("yParamType = " + Arrays.toString(yParamType));
}
}
// 运行结果
returnTypes = [class java.util.Random]
yParamType = [class java.lang.Number]
yParamType = [class java.lang.Boolean]
Process finished with exit code 0