java:类型变量(TypeVariable)解析--获取泛型类(Generic Class)所有的类型变量(TypeVariable)的实际映射类型

如何正确解析泛型类的类型变量(TypeVariable),获取对应的实际类型呢?
这是个小众话题,如果何感兴趣请继续往下阅读。
如下代码中定义了三个依次继承的类:

ServiceA
	└─ServiceB
		└─ServiceC
	public static class ServiceA<USER_T extends BaseUserBean,PAPER_T extends 
		public USER_T foo(USER_T input,USER_T[] level) {
			return null;
		}
		public void uoo(USER_T input,List<USER_T> level) {
		}
		public void xoo(List<T> input) {			
		}
	}
	public static class ServiceB<T extends Date> 
		extends ServiceA<UserBean, PaperBean,T>{
		@Override
		public UserBean foo(UserBean input, UserBean[] level) {
			return super.foo(input, level);
		}
		public void t2(T t) {}
	}
	public static class ServiceC extends ServiceB<java.sql.Date>{
	}
	public static class BaseUserBean{}
	public static class UserBean extends BaseUserBean{}
	public static class BasePaperBean{}
	public static class PaperBean extends BasePaperBean{}

程序编译时候ServiceC实例上所有泛型方法的参数变量(TypeVariable)都会匹配为实际的类型。比如ServiceA.uoo(USER_T input,List<USER_T> level)方法在经过泛型实例化的SerivceC中:对应的参数应该是uoo(UserBean,List<UserBean>)
这本是编译自动做的工作,一般来说我们不需要管它是怎么实现的。最近我的工作需要正确一个泛型类的方法正确的泛型参数并String输出,
就需要解决如何在程序中正确解析泛型类的类型变量(TypeVariable)。

getTypeVariables

具体的思路就:
如果父类是泛型类GenericClass
先调用Class.getTypeParameters()获取父类(泛型类)的类型变量(TypeVariable),
然后调用Class.clazz.getGenericSuperclass()得到父类的参数化类型(ParameterizedType)。
参数化类型中getActualTypeArguments()返回的数组是上面所有类型变量对应的实际类型参数。这样我们就得到了一个当前类的类型变量(TypeVariable)-实际类型(Type)的映射。
如果父类的父类还是泛型类,则依此类推。

实现代码如下

	/**
	 * [递归]返回指定类型上定义所有泛型变量及对应的实际的泛型参数的映射
	 * @param <M>
	 * @param clazz
	 * @param typeVariables [out]保存数据结果的映射对象 ,如果指定类型及所有父类型没有泛型变量则不会输出任何结果
	 * @return always typeVariables
	 */
	public static<M extends Map<TypeVariable<?>, Type> >  M getTypeVariables(Class<?> clazz,M typeVariables) {
		Class<?> superClass;
		if(null != clazz && (superClass = clazz.getSuperclass()) != null && null != typeVariables) {
			TypeVariable<?>[] typeParameters = superClass.getTypeParameters();
			/** 数组长度为0代表该类型没有定义类型变量,不是泛型类 */
			if(typeParameters.length>0) {
				ParameterizedType genericSuperClass = (ParameterizedType) clazz.getGenericSuperclass();
				Type[] typeArgs = ((ParameterizedType) genericSuperClass).getActualTypeArguments();
				for(int i=0;i<typeParameters.length;++i) {
					typeVariables.put(typeParameters[i], typeArgs[i]);
				}
				/** 父类递归 */
				return getTypeVariables(clazz.getSuperclass(),typeVariables);
			}
		}
		return typeVariables;
	}

上面的示例中,我们就得到了如下的一个映射:

T@ServiceB–>Date
USER_T@ServiceA–>UserBean
PAPER_T@ServiceA–>PaperBean
T@ServiceA–>T@ServiceB
注意: @后面代表该类型变量所属的类

getNormalizedTypeVariables

可以看到映射中有两个T分别是在ServiceA,ServiceB中定义的T@ServiceA-->T@ServiceB说明T@ServiceA对应的还是一个类型变量T@ServiceB并没有像其他类型变量样的映射到实际Class类型,而T@ServiceB才映射到Date
也就是说T@ServiceA间接映射到Date,实际的映射关系是这样的:
T@ServiceA-->T@ServiceB-->Date
如果要得到T@ServiceA真正的映射类型,还需要对映射进一步做归一化,对映射到类型变量的Key,从映射中循环查找,找到最终的映射类型。
如下是在getTypeVariables(Class<?> clazz,M typeVariables)方法基础上进一步对类型变量映射归一化的实现:

	public static LinkedHashMap<TypeVariable<?>, Type> getTypeVariables(Class<?> clazz) {
		return getTypeVariables(clazz,new LinkedHashMap<TypeVariable<?>, Type>());
	}
	/**
	 * 返回归一化的泛型变量-泛型参数映射<br>
	 * 如果Value是泛型变量({@link TypeVariable})则尝试以之为Key在表中查找对应的实际的泛型参数({@link ParameterizedType}或Class ),
	 * 如果找到则修改Value.
	 * @param clazz
	 * @param checkExisting 为{@code true}进行严格检查,如果映射中的Value是泛型变量且在映射中找不对应的泛型参数则抛出异常
	 * @return 泛型变量及对应的泛型参数的映射
	 * @see #getTypeVariables(Class, Map)
	 */
	public static Map<TypeVariable<?>, Type> getNormalizedTypeVariables(Class<?> clazz, boolean checkExisting) {
		 LinkedHashMap<TypeVariable<?>, Type> typeVariables = getTypeVariables(clazz);
		for(TypeVariable<?> typeVariable:typeVariables.keySet().toArray(new TypeVariable[typeVariables.size()])) {
			/** 泛型变量对应的实际泛型参数 */
			Type actualType = typeVariables.get(typeVariable);
			/** 如果Value是泛型变量,将值作为Key 循环查找直到找不到或类型不是泛型变量 */
			while(actualType instanceof TypeVariable) {
				actualType = typeVariables.get(actualType);
			}
			if(null == actualType) {
				/** 如果没有找到泛型变量对象的实际泛型参数且checkExisting为true抛出异常 */
				if(checkExisting) {
					StringBuilder builder = new StringBuilder("actualTypeArguments of ")
							.append(typeName(clazz)).append(":\r\n");
					for(Entry<TypeVariable<?>, Type> entry:typeVariables.entrySet()) {
						builder.append(typeName(entry.getKey()))
								.append("-->").append(typeName(entry.getValue())).append(":\r\n");
					}
					throw new IllegalArgumentException(String.format(
							"NOT FOUND actual TypeArgument for %s\n%s",
								typeName(typeVariable),builder));
				}

			}else if(!(actualType  instanceof TypeVariable)) {
				/** 将找到实际泛型参数替换原泛型变量值 */
				typeVariables.put(typeVariable, actualType);
			}
		}
		return typeVariables;
	}

getNormalizedTypeVariables方法得到的结果就是这样的:

T@ServiceB–>Date
USER_T@ServiceA–>UserBean
PAPER_T@ServiceA–>PaperBean
T@ServiceA–>Date
注意: @后面代表该类型变量所属的类

完整代码

以上完整实现代码参见码云仓库:
https://gitee.com/l0km/common-java/blob/master/common-base2/src/main/java/net/gdface/utils/ReflectionUtils.java

完整测试代码参见码云仓库:
https://gitee.com/l0km/common-java/blob/master/common-base2/src/test/java/net/gdface/utils/TypeVariableTest.java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值