【引言】
(灵感来自BaseQuickAdapter源码中的某个方法)
众所周知,由于泛型擦除机制,泛型在运行时不会保留。
但我认为上边这句话并不完全正确,举个简单的例子吧:
class A<T>{}
A<String> a1 = new A<>();
A<Integer> a2 = new A<>();
如上这两段代码在编译后, 泛型的实际类型String和Integer均不会保留
再来看下另外一种情况:
class A<T>{}
class B extends A<String>{}
class C extends A<Integer>{}
B b = new B();
C c = new C();
这种情况的泛型是会保留的,String和Integer均会被编译到class中,因为他们在类创建时已经确定了
总结:当泛型和类有一一对应关系时可获取泛型
故本文考虑的获取泛型实际类型都必须有一一对应的关系。
然后可能就有人要问了,既然我已经知道了B和C声明泛型的实际类型, 还有必要获取吗?
引言的问题
为了解答引言中最后的问题,需要再看一个例子:
//我有一个抽象的类-presenter
abstract class Presenter{}
class APresenter extends Presenter{
public void a(){}
}
class BPresenter extends Presenter{
public void b(){}
}
abstract class V<P extends Presenter>{
protected P mPresenter;
public V() {
mPresenter = createPresenter();
}
private P createPresenter() {
Class P = getPClass();//通过某种方式获取到泛型实际类型, 并且不需要子类提供泛型类型
try {
return (P) P.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
throw new RuntimeException(e);
}
}
private Class getPClass(){
// TODO 获取自己的泛型实际类型,前提是泛型在编译时已确认
return null;
}
}
class A extends V<APresenter>{
void onCreate(){
mPresenter.a();
}
}
class B extends V<BPresenter>{
void onCreate(){
BPresenter presenter = mPresenter;
presenter.b();
}
}
假如getPClass()
方法可以获取到子类的实际类型的话,我们每次创建一个 V 的子类就不需要 new Presenter
了。那么问题来了,如何获取类上声明泛型的实际类型呢?
完整代码
首先贴上最后完工的工具类实现代码,各位大佬们可以先看一看,后面会进行解释
public class Util{
/**
* ## 支持获取接口泛型
* owner: A.class, target: B.class
* 获取类B上声明的第 index 个泛型的在 类 A 中的实际类型
* @param owner (子类)泛型的拥有者
* @param index target声明的位置从左到右数第 index 个泛型
* @param target (父类)声明泛型的类, target 必须是 owner 的父类
* 例如:
* class<T> B{
* public Class getGenericClass(){
* return ReflectHelper.getGenericClass(getClass(), B.class, 0);
* }
* }
* class A extends B<String>{
*
* }
* class C extends B<Integer>{
*
* }
* class D<A> extends B<A>{
*
* }
*
* System.out.println(new A().getGenericClass() instanceOf String.class);
* System.out.println(new C().getGenericClass() instanceOf Integer.class);
* System.out.println(new D<Double>().getGenericClass() instanceOf Double.class);
* 打印结果为:
* true
* true
* false
*/
public static<T> Class getGenericClassSupportInterface(@NonNull Class<? extends T> owner, @NonNull Class<T> target, int index){
if(owner == target)
return null;
if(!target.isAssignableFrom(owner))
return null;
TypeVariable<Class<T>>[] tps = target.getTypeParameters();
if(index >= tps.length)
return null;
class Cache{
private Cache(Class clazz) {
this.clazz = clazz;
}
private Cache(Class clazz, int index) {
this.clazz = clazz;
this.index = index;
}
@NonNull
@Override
public String toString() {
return clazz.getName() + "-@-" + index;
}
private Class clazz;
private int index = 0;
}
LinkedList<Cache> classes = new LinkedList<>();
classes.add(new Cache(owner));
Class nextClazz = owner;
outside:
while (true){
Class superClazz = nextClazz.getSuperclass();
if(superClazz == target)
break;
if(superClazz != null && target.isAssignableFrom(superClazz)){
classes.add(new Cache(superClazz));
}else {
Class[] interfaces = nextClazz.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class interfaca = interfaces[i];
if(interfaca == target)
break outside;
if(target.isAssignableFrom(interfaca)){
classes.add(new Cache(interfaca, i));
superClazz = interfaca;
break;
}
}
}
nextClazz = superClazz;
if(superClazz == null)
break;
}
Cache cache = classes.removeLast();
Class supClazz = cache.clazz;
Type typeVar;
Class last = target;
if(last.isInterface()){
typeVar = ((ParameterizedType) supClazz.getGenericInterfaces()[cache.index]).getActualTypeArguments()[index];
}else {
typeVar = ((ParameterizedType) supClazz.getGenericSuperclass()).getActualTypeArguments()[index];
}
if(typeVar instanceof Class)
return (Class) typeVar;
while (!classes.isEmpty()) {
System.out.println(cache);
index = Util.indexOf(supClazz.getTypeParameters(), typeVar, new Util.Comparator<TypeVariable, Type>() {
@Override
public boolean equals(TypeVariable left, Type right) {
if(left == right)
return true;
if(left == null || right == null)
return false;
if(!(right instanceof TypeVariable))
return false;
String leftName = left.getName();
String rightName = ((TypeVariable) right).getName();
return leftName.equals(rightName);
}
});
last = supClazz;
cache = classes.removeLast();
supClazz = cache.clazz;
System.out.println(cache + "___");
if(last.isInterface()){
typeVar = ((ParameterizedType) supClazz.getGenericInterfaces()[cache.index]).getActualTypeArguments()[index];
}else {
typeVar = ((ParameterizedType) supClazz.getGenericSuperclass()).getActualTypeArguments()[index];
}
if(typeVar instanceof Class)
return (Class) typeVar;
}
return null;
}
static class Util{
//获取数组 array 中 object 的角标
public static <L, R> int indexOf(L[] array, R object, @NonNull Comparator<L, R> comparator) {
if (array == null)
return -1;
for (int i = 0; i < array.length; i++) {
if (comparator.equals(array[i], object))
return i;
}
return -1;
}
interface Comparator<L, R> {
boolean equals(L left, R right);
}
}
}
知识储备
clazz.getTypeParameters()
首先我们要了解这个方法的含义TypeVariable<? extends Class<? extends TV>>[] variables = clazz.getTypeParameters();
,先来看一段代码:
如上代码,我给类TV声明了两个泛型V和T,而通过getTypeParameters()
获取到的数组正代表类头部声明的泛型,我们可以通过getName()
方法获取到声明时定义的字符串(这个例子中就是 V和T)
clazz.getGenericSuperclass()
先来看一段代码:
clazz.getGenericSuperclass()
的返回值携带了父类的泛型,而通过getActualTypeArguments()
可以获取到这个泛型,如果instanceof Class
是true,则此泛型有实际类型,为false则此类型是 TypeVariable
,即还是泛型。
clazz.getGenericInterfaces()
我们再来看一个例子
与getGenericSuperclass
类似,只不过这个方法获取的是接口信息,因为接口可以实现多个,所以返回值是一个数组。如上图,可通过此方法可以获取到所有接口的泛型。
superClazz.isAssignableFrom(SubClazz)
父类Class.isAssignableFrom(子类.class), 来判断父类是否是子类的父类或父接口,返回值boolean类型
思路
再举个例子,用时序图分析:
class Super<T>{
public static void main(String[] args){
new FinalSub().print();//结果为:java.lang.String
}
protected void print(){
Class clazz = Util.getGenericClassSupportInterface(getClass(), Super.class, 0);
if(clazz != null)
System.out.println(clazz.getName());
}
}
class Sub<T1,T2> extends Super<T1>{}
class SubSub<T1> extends Sub<T1, Integer>{}
class FinalSub extends SubSub<String>{}
因为已知条件是父类上泛型声明的角标(就是传入的index参数),所以泛型要从父类往下找,但由于父类可能有多个子类,不能确定子类是否使我们要找到目标类,所以要从子类出发,通过方法isAssignableFrom
往上遍历寻找父类。
然后从最顶层我们找到的父类出发,根据已知的索引index和
直接子类.getGenericInterfaces().getActualTypeArguments()[index]
方法寻找,如果返回值类型是Class,则说明我们已将找到目标泛型的实际类型了,如果返回值类型是TypeVariable
,则说明这个直接子类定义了泛型,目标泛型的实际类型在直接子类的子类中,所以我们需要通过遍历getTypeParameters() 返回值TypeVariable[]
,使用函数 TypeVariable.getName()
与 刚才我们通过 index找到的 TypeVariable
对比Name, 便能找到其泛型的角标,然后在直接子类的子类中,通过
直接子类的子类.getGenericInterfaces().getActualTypeArguments()[index]
方法寻找,如果返回值类型是Class,则说明我们已将找到目标泛型的实际类型了,如果返回值类型是TypeVariable
,则继续循环向下寻找。
代码分析
if(owner == target)
return null;
if(!target.isAssignableFrom(owner))
return null;
边界检查,过滤传入同一个class或传入两个非继承实现关系的class
TypeVariable<Class<T>>[] tps = target.getTypeParameters();
if(index >= tps.length)
return null;
class Cache{
private Cache(Class clazz) {
this.clazz = clazz;
}
private Cache(Class clazz, int index) {
this.clazz = clazz;
this.index = index;
}
@NonNull
@Override
public String toString() {
return clazz.getName() + "-@-" + index;
}
private Class clazz;
private int index = 0;
}
LinkedList<Cache> classes = new LinkedList<>();
classes.add(new Cache(owner));
Class nextClazz = owner;
outside: //使用标号语句以便能跳出两层循环
while (true){
Class superClazz = nextClazz.getSuperclass();
if(superClazz == target)
break;
if(superClazz != null && target.isAssignableFrom(superClazz)){//处理父类的情况
classes.add(new Cache(superClazz));
}else {//处理父接口的情况
Class[] interfaces = nextClazz.getInterfaces();//获取实现的所有接口
for (int i = 0; i < interfaces.length; i++) {
Class interfaca = interfaces[i];
if(interfaca == target)
break outside;
if(target.isAssignableFrom(interfaca)){//找到目标父接口
classes.add(new Cache(interfaca, i));//存入链表中,继续向上寻找
superClazz = interfaca;
break;
}
}
}
nextClazz = superClazz;
if(superClazz == null)
break;
}
这段代码是循环向上寻找父类/父接口的逻辑,注意这里考虑到了父接口的情况,所以在Cache中缓存了一个index变量,用来保存所继承/实现的父接口的位置, 以便向下寻找泛型时使用。
剩余的代码都是从上到下寻找接口泛型实际类型的逻辑,通过 TypeVariable.getName()
寻找index,根据index寻找泛型或泛型实际类型,循环直到找到实际类型。注意:由于接口多继承的原因,需要用到上边存储index来找目标接口上的泛型。((ParameterizedType) supClazz.getGenericInterfaces()[cache.index]).getActualTypeArguments()[index];