主题
如何获取嵌套的泛型类型?
场景
假如需要将一个json转化为对象,此时就需要拿到该对象对应的class。如果这个class中所有的字段都是已知类型,比如Integer、String、User引用类型等等,此时通过反射即可以轻松获取该class中各字段对应的数据类型,然后只需通过反射创建对应的对象并按照字段名进行赋值即可。但假如该class类上定义了一个泛型,比如Response.class的定义为:
public class Response<E> {
private Integer code;
private E data;
}
而E有可能又是一个泛型的嵌套,比如List<Long>
,此时如果想将那个json转为Response<List<Long>>
应该如何处理呢?在解析json时是不是必须要拿到要转化成的目标对象的具体类型呢?否则如何将json数据对应类字段进行同类型赋值呢?
理解
java中,泛型的应用可以分为 泛型定义 和 泛型使用。
- 泛型定义:
接口、类、[静态]方法、字段 使用泛型进行类型填充;比如上面定义的Response<E>
,这里的E就是一个泛型定义占位符,它的上限默认是Object(编译时泛型擦除会擦除到类型上限),而data字段也使用了E去占位,这仍属于泛型定义的范畴。 - 泛型使用:
相比于泛型的定义,大部分人会更熟悉泛型的使用(两者的区别类似于方法定义时声明的形参与方法使用时对形参的赋值)。最常见的例子就是List<E>
,当我们需要使用List时,我们可以通过泛型指定该list具体可以存放的数据类型, 比如List<String> list = new ArrayList<>();
,表示他将用于存储一些字符串对象,这其实就是对List.class泛型的使用,在使用list时(创建List对象),我们将想让list存放的数据的类型通过泛型窗口<>“告诉它”,这时泛型的具体值(类型)就被确定了下来。具体类型的确定对于方法的内部其实并没有实质性的意义,因为使用了泛型的方法,内部一定是一段对E属于任何类型都通用的逻辑;但在使用层面,根据我们传入泛型窗口的具体类型,我们就可以基于泛型避免很多运行时才可能发现的错误,比如类型检查、类型强转等等,当然,这也是发明泛型最初的目的。如果说()是方法的传参窗口,那么<>就是泛型的传参窗口,泛型的发明很大程度上就是借鉴了方法传参的思想。
那么我们获取一个类的泛型类型呢?
假如我们给一个类定义好了泛型E,当我们在使用这个类时,我们一般就会将泛型对应的具体类型通过泛型窗口<>传递给这个类创建出的对象(类似于方法赋值),此时我们就可以通过jdk提供的API[Class.getGenericSuperclass()]成功获取类的泛型信息 – ParameterizedType。比如你现在将Response泛型声明为Response<List<Long>>
,如果你想拿到他的泛型类型 --> List,再拿到List的泛型类型 --> Long,使用上述API就可以去轻松实现。 考虑到这是一个很常见的场景,于是Jackson将上面两个API封装成了一个TypeReference抽象类,下面是这个类的源码:
public abstract class TypeReference<T> implements Comparable<TypeReference<T>> {
protected final Type _type;
protected TypeReference() {
Type superClass = this.getClass().getGenericSuperclass();
if (superClass instanceof Class) {
throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
} else {
this._type = ((ParameterizedType)superClass).getActualTypeArguments()[0];
}
}
public Type getType() {
return this._type;
}
public int compareTo(TypeReference<T> o) {
return 0;
}
}
TypeReference将获取泛型的动作写在了构造器中,当我们使用TypeReference并创建他的对象时,会通过他的泛型窗口将具体类型传递进去,内部拿到我们传递进去的具体类型,就可以按类型创建具体对象并进行字段赋值了。