在使用springmvc时,咱们一般会定义相似这样的通用类与前端进行交互,以便于前端能够作一些统一的处理:html
public class Result<T> { private int ret; private String msg; private T data; // 此处省略getter和setter方法 }
这样的类序列化为json后,js反序列化处理起来毫无压力。可是若是rest接口的消费端就是java呢,java泛型的类型擦除却容易引入一些障碍。前端
先定义一个类,后面的例子会用到:java
public class Item { private String name; private String value; // 此处省略getter和setter方法 }
JSON数据:spring
{ "data":{ "name":"username", "value":"root" }, "msg":"Success", "ret":0 }
当拿到上面的数据时,咱们想到其对应的类型是Result<Item>
,因此得想办法将这个json数据反序列化为这个类型才行。json
JSONObject.parseObject(json, Result<Item>.class);
,编译器就报错了Cannot select parameterized type
。数组
JSONObject.parseObject(json, Result.class);
,执行没问题。可是没有Item类型信息,fastjson不可能跟你心有灵犀一点通知道该把data转为Item类型,result.getData().getClass()
结果是com.alibaba.fastjson.JSONObject
,也算是个妥善处理吧。mvc
找了一下前人的经验,使用TypeReference来处理,JSONObject.parseObject(json, new TypeReference<Result<Item>>(){});
,终于“完美”解决!函数
有了v3的经验,觉得找到了通用处理的捷径,遂封装了一个处理这种类型的工具方法:工具
private static <T> Result<T> parseResultV1(String json) { return JSONObject.parseObject(json, new TypeReference<Result<T>>() { }); }
而且把采用v3的地方改用了此parseResult方法:测试
Result<Item> result = parseResultV1(json);
觉得万事大吉,连测都没测试就把代码提交了。测都不测试,固然难以有好结果了:
System.out.println(result.getData()); // java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to Item
很显然parseResultV1把Item的类型信息丢掉了。
{ "data":"Hello,world!", "msg":"Success", "ret":0 }
试了一下Result
String data = (String)result.getData(); System.out.println(data);
原来TypeReference的构造器是能够传入参数的,
private static <T> Result<T> parseResultV2(String json, Class<T> clazz) { return JSONObject.parseObject(json, new TypeReference<Result<T>>(clazz) { }); }
这个能够真的能够完美反序列化Result<Item>
了。
后来发现parseResultV2没法处理相似Result<List<T>>
,原来TypeReference没法处理嵌套的泛型(这里指的是类型参数未肯定,而不是相似Result<List<Item>>
类型参数已经肯定)。借用Fastjson解析多级泛型的几种方式—使用class文件来解析多级泛型里的方法,新增长一个专门处理List类型的方法:
private static <T> Result<List<T>> parseListResult(String json, Class<T> clazz) { return JSONObject.parseObject(json, buildType(Result.class, List.class, Item.class)); } private static Type buildType(Type... types) { ParameterizedTypeImpl beforeType = null; if (types != null && types.length > 0) { for (int i = types.length - 1; i > 0; i--) { beforeType = new ParameterizedTypeImpl(new Type[]{beforeType == null ? types[i] : beforeType}, null, types[i - 1]); } } return beforeType; }
或者根据这里只有两层,简单以下:
private static <T> Result<List<T>> parseListResult(String json, Class<T> clazz) { ParameterizedTypeImpl inner = new ParameterizedTypeImpl(new Type[]{clazz}, null, List.class); ParameterizedTypeImpl outer = new ParameterizedTypeImpl(new Type[]{inner}, null, Result.class); return JSONObject.parseObject(json, outer); }
todo: 上面两个方法已经能够知足现有须要,有时间再看看可否将两个方法统一为一个。
看看TypeReference的源码:
protected TypeReference(Type... actualTypeArguments) { Class<?> thisClass = this.getClass(); Type superClass = thisClass.getGenericSuperclass(); ParameterizedType argType = (ParameterizedType)((ParameterizedType)superClass).getActualTypeArguments()[0]; Type rawType = argType.getRawType(); Type[] argTypes = argType.getActualTypeArguments(); int actualIndex = 0; for(int i = 0; i < argTypes.length; ++i) { if (argTypes[i] instanceof TypeVariable) { argTypes[i] = actualTypeArguments[actualIndex++]; if (actualIndex >= actualTypeArguments.length) { break; } } } Type key = new ParameterizedTypeImpl(argTypes, thisClass, rawType); Type cachedType = (Type)classTypeCache.get(key); if (cachedType == null) { classTypeCache.putIfAbsent(key, key); cachedType = (Type)classTypeCache.get(key); } this.type = cachedType; }
实际上它首先获取到了泛型的类型参数argTypes,而后遍历这些类型参数,若是遇到是TypeVariable
类型的则用构造函数传入的Type将其替换,而后此处理后的argTypes基于ParameterizedTypeImpl构造出一个新的Type,这样的新的Type就能够具有咱们期待的Type的各个泛型类型参数的信息了。因此fastjson就可以符合咱们指望地反序列化出了Result<Item>
。
正是因为这个处理逻辑,因此对于v6里的Result<List<T>>
就没法处理了,它只能处理单层多类型参数的状况,而没法处理嵌套的泛型参数。
没找到TypeReference的有参构造函数用法的比较正式的文档,可是基于源码的认识,咱们应该这么使用TypeReference的有参构造函数:
new TypeReference<Map<T1, T2>>(clazz1, clazz2){} new TypeReference<Xxx<T1, T2, T3>>(clazz1, clazz2, clazz3){}
也就是构造器里的Type列表要与泛型类型参数一一对应。
那至于ParameterizedTypeImpl
怎么回事呢?
import java.lang.reflect.ParameterizedType; // ...其余省略... public class ParameterizedTypeImpl implements ParameterizedType { public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType){ this.actualTypeArguments = actualTypeArguments; this.ownerType = ownerType; this.rawType = rawType; } // ...其余省略... }
之前也没了解过ParameterizedType,与它相关的还有
Type 全部已知子接口: GenericArrayType, ParameterizedType, TypeVariable<D>, WildcardType 全部已知实现类: Class
先看看此次已经用到的ParameterizedType接口(下列注释是从jdk中文文档拷贝过来,不太好理解)
public interface ParameterizedType extends Type { //返回表示此类型实际类型参数的 Type 对象的数组。 //注意,在某些状况下,返回的数组为空。若是此类型表示嵌套在参数化类型中的非参数化类型,则会发生这种状况。 Type[] getActualTypeArguments(); //返回 Type 对象,表示此类型是其成员之一的类型。 Type getOwnerType(); //返回 Type 对象,表示声明此类型的类或接口。 Type getRawType(); }
结合ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType)
的例子来理解:
new ParameterizedTypeImpl(new Type[]{clazz}, null, List.class)
用于构造List<T>
。
泛型是Java SE 1.5的新特性,Type
也是1.5才有的。它是在java加入泛型以后为了扩充类型引入的。与Type相关的一些类或者接口来表示与Class相似可是又因泛型擦除丢失的一些类型信息。