获取泛型实际类型

【引言】
(灵感来自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声明了两个泛型VT,而通过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];

猜你喜欢

转载自blog.csdn.net/qq_27070117/article/details/103069308