这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战
一、介绍
Type接口是Java中所有类型的公共高级接口,是由JDK5引入,主要是为了实现参数化类型(泛型),Class类是该接口的直接实现类。
在日常开发过程中,一些公共抽象点需要使用泛型进行结构抽象,从而达到业务调用点灵活调用的目的。之前有一次由于不懂Type接口以及子类,导致部分抽象做的不够灵活,本篇主要记录Type接口下参数化类型ParameterizedType的一些API使用。
二、子接口
从左到右依次是直接实现类Class,数组类型接口GenericArrayType,参数化类型接口ParameterizedType,通配符表达式接口WildcardType,类型变量接口(泛指任何类)TypeVariable。大概知道了几个接口之间的关系之后,直接进入一个应用场景。
上篇中服务端与客户端交互时设计了一个固定的数据结构,其中该数据结构中的存在两个属性String dataJson和T Data,而客户端只会传输经过一定的编码或者加密处理dataJson字段,由服务端自行处理字符串数据。最理想的情况就是,服务端做一层统一处理,接收到该实体对象之后,根据服务端和客户端之间的约定,解析其中的dataJson为对应的data数据。上篇文章介绍了如何使用参数处理器处理这一场景,当时同事最开始提出这个问题的时候还并没有提出想将json转为实体,仅想通过某种统一处理,将json解码(客户端传输过来的是经过某种编码)。最终选择的方案是通过RequestBodyAdvice,当完成解码这个动作之后,同事发现,既然都通过统一处理json串解码了,那不就可以通过泛型的方式把json转换为对应的实体?我原本以为获取不到对应类的泛型,而恰巧(真的是巧)。RequestBodyAdvice#afterBodyRead方法上参数是Type类型,从而能通过它(如果是泛型则类型为ParameterizedType)就可以获取到对应类的泛型类型,从而进行转换为对应的实体。
public Object afterBodyRead(Object body, HttpInputMessage inputMessage,
MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType)
这里我想抛出我的问题:如果这里没有Type参数,可以看之前那篇文章的其他两种方式,比如假设现在的场景就是使用@InitBuilder的方式最合适,要怎么获取到Type,或者说获取到参数的ParameterizedType?
查找了一些资料,对查到的一些写法的分析:
// 1. 直接Class: 这种写法纯属骗人,从类图可以看出,就算用父接口引用,其实本质上还是Class类型
// 和ParameterizedType类型不符。
DataEntity<User> dataEntity = new DataEntity<User>();
Type t = (Type)dataEntity.getClass();
// 2. 通过class.getGenericSuperclass():这种方式可以获取到的的确是ParameterizedType,
// 但那是父类的ParameterizedType(如果父类是泛型的情况)。
// 那么如果A继承B,B能获取到A的class, 再通过A的class获取到B的泛型参数, 虽然有点绕, 但也不失为一种方式。
List<String> list = new ArrayList<String>();
Type type =list.getClass().getGenericSuperclass();
// 3.通过反射获取: 反射是根据类来获取对应的属性,这种方式下获取的是泛型前的类型TypeVariable
// 如果某个方法中的参数是泛型类型, 通过method.getGenericParameterTypes()可以获取到ParameterizedType
DataEntity<User> dataEntity = new DataEntity<User>();
try{
Field field = dataEntity.getClass().getField("data");
Type paraType = field.getGenericType();
System.out.println(paraType);
}catch(Exception e){
e.printStackTrace();
}
根据以上的3种方法,1和3直接就不对。只有2沾点边。所以在设计上可以将A设计为泛型接口,B作为A的具体实现,这时就可以获取到对应的泛型类型。
@Test
public void testAA(){
B b = new B();
Type[] type = b.getClass().getGenericInterfaces();
System.out.println(((ParameterizedType)type[0]).getActualTypeArguments()[0]);
}
interface A<T>{
T getData();
void setData(T data);
}
class B implements A<User>{
@Override
public User getData() {
return null;
}
@Override
public void setData(User data) {
}
}
三、总结
以上是对上篇SpringMvc请求参数二次处理的一些思考,所以在处理这些问题时,需要对Class和Type的相关接口以及结构有一定的了解。