反射获取泛型类的实际参数 泛型用得还是比较多的,那么如何获取泛型类上实际的参数类型呢?
比如一个接口为
1 2 public interface IBolt <T , K > {}
现在给一个IBolt的具体实现类,可以获取到实际的参数类型么?下面几种case可以怎么获取实际的IBolt中的T和K类型呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ABolt implements IBolt <String , Boolean > {}public class AFBolt <T > implements IBolt <String , T > {}public interface EBolt <T > extends IBolt <String , T > {}public class AEBolt implements EBolt <Boolean > {}public interface RBolt extends IBolt <String , Boolean > {}public class ARBolt implements RBolt {}public abstract class AbsBolt <T ,K > implements IBolt <T ,K > {}public class BBolt extends AbsBolt <String , Boolean > {}public abstract class EAbsBolt <T > implements IBolt <String , T > {}public class BEBolt extends EAbsBolt <Boolean > {}
I. 基本姿势 首先拿最简单的两个case来进行分析,一个是 ABolt, 一个是BBolt,根据这两个类信息来获取对应的泛型类型;
1. 接口实现方式获取 主要借助的就是右边这个方法:java.lang.Class#getGenericInterfaces
a. 简单对比
Type[] getGenericInterfaces
以Type的形式返回本类直接实现的接口.这样就包含了泛型参数信息
Class[] getInterfaces
返回本类直接实现的接口.不包含泛型参数信息
b. 编码实现 一个基础的实现方式如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void testGetTypes () { Type[] types = ABolt.class .getGenericInterfaces () ; ParameterizedType ptype; for (Type type: types) { if (!(type instanceof ParameterizedType)) { continue ; } ptype = (ParameterizedType) type; if (IBolt.class .equals (ptype .getRawType ())) { Type[] parTypes = ptype.getActualTypeArguments(); for (Type par: parTypes) { System.out.println(par.getTypeName()); } } } }
简单分析上面实现:
首先是获取所有的接口信息,遍历接口,
如果这个接口是支持泛型的,则返回的type应该是ParameterizedType
类型
获取原始类信息(主要目的是为了和目标类进行对比 IBolt.class.equals(ptype.getRawType())
)
获取泛型类型 ptype.getActualTypeArguments()
输出结果如下:
1 2 java.lang.String java.lang.Boolean
上面这个实现针对ABolt还可以,但是换成 AEBolt 之后,即非直接实现目标接口的情况下,发现什么都获取不到,因为 IBolt.class.equals(ptype.getRawType())
这个条件不会满足,稍稍改一下,改成只要是IBolt的子类即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void testGetTypes () { Type[] types = AEBolt.class .getGenericInterfaces () ; ParameterizedType ptype; for (Type type: types) { if (!(type instanceof ParameterizedType)) { continue ; } ptype = (ParameterizedType) type; if (ptype.getRawType() instanceof Class && IBolt.class .isAssignableFrom ((Class <?>) ptype .getRawType ())) { Type[] parTypes = ptype.getActualTypeArguments(); for (Type par: parTypes) { System.out.println(par.getTypeName()); } } } }
此时输出为如下,实际上只是EBolt上的泛型类型,与我们期望的输出 (String, Boolean) 不符,后面再说
2. 抽象类继承方式获取 抽象类与接口的主要区别在于类是单继承的,所以改成用 java.lang.Class#getGenericSuperclass
获取
a. 简单对比
Type getGenericSuperclass()
返回父类的基本类信息,包含泛型参数信息
Class<? super T> getSuperclass();
返回父类信息,不包含泛型
b. 代码实现 同上面的差不多,针对BBolt的实现,可以这么来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Test public void testGetAbsTypes () { Class basicClz = BBolt.class ; Type type; ParameterizedType ptype; while (true ) { if (Object.class .equals (basicClz )) { break ; } type = basicClz.getGenericSuperclass(); if (!(type instanceof ParameterizedType)) { basicClz = basicClz.getSuperclass(); continue ; } ptype = (ParameterizedType) type; if (ptype.getRawType() instanceof Class && IBolt.class .isAssignableFrom ((Class <?>) ptype .getRawType ())) { Type[] parTypes = ptype.getActualTypeArguments(); for (Type par : parTypes) { System.out.println(par.getTypeName()); } break ; } else { basicClz = basicClz.getSuperclass(); } } }
针对上面代码简单进行分析,步骤如下:
获取父类(包含泛型)信息
如果父类没有泛型信息,则继续往上获取父类信息
包含泛型信息之后,判断这个类是否为我们预期的目标类 IBolt.class.isAssignableFrom((Class<?>) ptype.getRawType())
如果是,则直接获取参数信息
输出结果如下:
1 2 java.lang.String java.lang.Boolean
当然上面依然是存在和上面一样的问题,对于BEBolt这个类,输出的就和我们预期的不同,其输出只会有 EAbsBolt<Boolean>
上的信息,即到获取EAbsBolt这一层时,就结束了
如果我们将上面的判定当前类是否为Ibolt.class,会输出什么呢?
什么都没有,因为Ibolt是接口,而获取父类是获取不到接口信息的,所以判定永远走不进去
II. 进阶实现 上面的基础实现中,都存在一些问题,特别是但继承结构比较复杂,深度较大时,其中又穿插着泛型类,导致不太好获取精确的类型信息,下面进行尝试探索,不保证可以成功
1. 接口实现方式 主要的目标就是能正常的分析AEBolt这个case,尝试思路如下:
改进后的实现如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Test public void testGetTypes () { Class basicClz = AEBolt.class ; Type[] types; ParameterizedType ptype; types = basicClz.getGenericInterfaces(); boolean loop = false ; while (true ) { if (types.length == 0 ) { break ; } for (Type type : types) { if (type instanceof Class) { if (IBolt.class .isAssignableFrom ((Class <?>) type )) { types = ((Class) type).getGenericInterfaces(); loop = true ; break ; } else { continue ; } } ptype = (ParameterizedType) type; if (ptype.getRawType() instanceof Class) { if (!IBolt.class .isAssignableFrom ((Class <?>) ptype .getRawType ())) { continue ; } if (IBolt.class .equals (ptype .getRawType ())) { Type[] parTypes = ptype.getActualTypeArguments(); for (Type par : parTypes) { System.out.println(par.getTypeName()); } return ; } else { types = ((Class) ptype.getRawType()).getGenericInterfaces(); loop = true ; break ; } } } if (!loop) { break ; } } }
上面的实现相比较之前的负责不少,首先来看针对 AEBolt 而言,输出为
如果改成 ARBolt, 即RBolt这个接口在继承IBolt接口的同时,指定了参数类型,这时输出如
1 2 java.lang.String java.lang.Boolean
也就是说这个思路是可以的,唯一的问题就是当实现目标接口的某一层接口,也是泛型时,直接定位到最底层,获取的就是T,K这种符号参数了,因为实际的类型参数信息,在上一层定义的
那么有没有办法将这个参数类型传递下去呢?
实际尝试了一下,再往下走就比较复杂了,感觉有点得不偿失,不知道是否有相关的工具类
2. 继承类方式 接口方式实现之后,继承类方式也差不多了,而且相对而言会更简单一点,因为继承是单继承的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Test public void testGetAbsTypes () { Class basicClz = BEBolt.class ; Type type; ParameterizedType ptype; while (true ) { if (Object.class .equals (basicClz )) { break ; } type = basicClz.getGenericSuperclass(); if (!(type instanceof ParameterizedType)) { basicClz = basicClz.getSuperclass(); continue ; } ptype = (ParameterizedType) type; if (Object.class .equals (basicClz .getSuperclass ().getSuperclass ())) { Type[] parTypes = ptype.getActualTypeArguments(); for (Type par : parTypes) { System.out.println(par.getTypeName()); } break ; } else { basicClz = basicClz.getSuperclass(); } } }
输出如下,同样有上面的问题
III. 小结 通过反射方式,后去泛型类的参数信息,有几个有意思的知识点:
获取泛型类信息
1 2 3 4 5 java.lang.Class#getGenericSuperclass java.lang.Class#getGenericInterfaces java.lang.reflect.ParameterizedType#getActualTypeArguments
Class判断继承关系
1 2 java.lang.Class#isAssignableFrom
II. 其他 一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
声明 尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
扫描关注