Type接口

178 阅读4分钟

这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战

一、介绍

Type接口是Java中所有类型的公共高级接口,是由JDK5引入,主要是为了实现参数化类型(泛型),Class类是该接口的直接实现类。

​ 在日常开发过程中,一些公共抽象点需要使用泛型进行结构抽象,从而达到业务调用点灵活调用的目的。之前有一次由于不懂Type接口以及子类,导致部分抽象做的不够灵活,本篇主要记录Type接口下参数化类型ParameterizedType的一些API使用。

二、子接口

3LURqsICMOANz7e

从左到右依次是直接实现类Class,数组类型接口GenericArrayType,参数化类型接口ParameterizedType,通配符表达式接口WildcardType,类型变量接口(泛指任何类)TypeVariable。大概知道了几个接口之间的关系之后,直接进入一个应用场景。

上篇中服务端与客户端交互时设计了一个固定的数据结构,其中该数据结构中的存在两个属性String dataJsonT 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请求参数二次处理的一些思考,所以在处理这些问题时,需要对ClassType的相关接口以及结构有一定的了解。