前言
Jackson是一个比较流行的Json序列化和反序列化框架。本文以Jackson为例介绍TypeReference实现涉及泛型的反序列化,及TypeReference的实现原理。对于获取泛型类型信息的场景,TypeReference是一个可以参考的通用解决方案。
实例
Jackson ObjectMapper的readValue可以将Json字符串反序列化为Java对象。如下例中将[{"id":null,"name":" ","age":500,"gender":false,"email":"email","employed":true,"salary":10}]
反序列化为List<UserResource>
类型。
Json字符串:
1 2 3 4 5 6 7 8 9 | [{ "id" : null , "name" : " " , "age" : 500, "gender" : false , "email" : "email" , "employed" : true , "salary" : 10 }] |
UserResource实体类:
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Builder @Data @NoArgsConstructor @AllArgsConstructor public class UserResource { private UUID id; private String name; private int age; private boolean gender; private String email; private boolean employed; private BigDecimal salary; } |
实现
理想的实现方式
理想的实现方式是告诉ObjectMapper
的readValue
方法,我要的是List<UserResource>
,帮我反序列化成这个类型。
1 | List<UserResource> list = new ObjectMapper().readValue(userResourcesStr, List<UserResource>. class ); |
现实是编译器告诉你这不行,Cannot select from parameterized type
. 也很好理解,Java编译器认为List是Class,而List则不是。
换一种方式实现
既然不能用List<UserResource>.class
, 那如果我告诉ObjectMapper
的readValue
方法,我要的是List
类型,但返回值类型是List<UserResource>
, 会发生什么呢?
1 | List<UserResource> list = new ObjectMapper().readValue(userResourcesStr, List. class ); |
这时候倒没有编译错误, 但是会有警告:Unchecked assignment: 'java.util.List' to 'java.util.List<UserResource>'
, 显然ObjectMapper并不能反序列化为UserResource类型,而是LinkedHashMap类型。如下图所示:
TypeReference的实现方式
ObjectMapper提供了readValue(String content, TypeReference valueTypeRef)
接口,第二个参数为new一个TypeReference
的子类实例:new TypeReference<List<UserResource>>(){}
。泛型抽象类TypeReference用于通过子类获取完整的泛型类型信息。
1 2 | public <T> T readValue(String content, TypeReference valueTypeRef) List<UserResource> list = new ObjectMapper().readValue(userResourcesStr, new TypeReference<List<UserResource>>(){}); |
TypeReference 实现原理
上例中new TypeReference<List<UserResource>>(){}
子类的实例,TypeReference
源码部分比较简单,主要逻辑是,通过getClass().getGenericSuperclass()
获取父类中的参数化类型(ParameterizedType):
TypeReference主要源码:
1 2 3 4 5 | protected TypeReference() { Type superClass = getClass().getGenericSuperclass(); _type = ((ParameterizedType) superClass).getActualTypeArguments()[ 0 ]; } |
getGenericSuperclass返回一个Type类型的对象,代表实体(class, interface, primitive type or void)的直接父类,如果父类是参数化类型,则返回的Type对象可准确反映源代码中使用的实际type参数。
Class的genericInfo:
解说
其中核心的方法是:getActualTypeArguments,它可以得到父类的反省类型
ParameterizedType是一个记录类型泛型的接口,继承自Type,一共三方法:
- Type[] getActualTypeArguments(); // 返回泛型类型数组
- Type getRawType(); // 返回原始类型Type
- Type getOwnerType(); // 返回 Type 对象,表示此类型是其成员之一的类型。
例如 Map<String,String> 对应的ParameterizedType三个方法分别取值如下:
- [class java.lang.String, class java.lang.String]
- interface java.util.Map
- null
例证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package JsonLearn; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; public class TypeReferencBaseLearn { public static class IntMap extends HashMap<String, Integer> {} void test1() { IntMap intMap = new IntMap(); System.out.println( "getSuperclass:" + intMap.getClass().getSuperclass()); System.out.println( "getGenericSuperclass:" + intMap.getClass().getGenericSuperclass()); Type type = intMap.getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType)type; for (Type t : p.getActualTypeArguments()) { System.out.println(t); } } } void test2() { Map<String, Integer> intMap = new HashMap<>(); System.out.println( "\ngetSuperclass:" + intMap.getClass().getSuperclass()); System.out.println( "getGenericSuperclass:" + intMap.getClass().getGenericSuperclass()); Type type = intMap.getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType)type; for (Type t : p.getActualTypeArguments()) { System.out.println(t); } } } void test3() { Map<String, Integer> intMap = new HashMap<String, Integer>(){}; System.out.println( "\ngetSuperclass:" + intMap.getClass().getSuperclass()); System.out.println( "getGenericSuperclass:" + intMap.getClass().getGenericSuperclass()); Type type = intMap.getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType)type; for (Type t : p.getActualTypeArguments()) { System.out.println(t); } } } public static void main(String[] args) { TypeReferencBaseLearn obj = new TypeReferencBaseLearn(); obj.test1(); obj.test2(); obj.test3(); } } |
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | getSuperclass: class java.util.HashMap getGenericSuperclass:java.util.HashMap<java.lang.String, java.lang.Integer> class java.lang.String class java.lang.Integer getSuperclass: class java.util.AbstractMap getGenericSuperclass:java.util.AbstractMap<K, V> K V getSuperclass: class java.util.HashMap getGenericSuperclass:java.util.HashMap<java.lang.String, java.lang.Integer> class java.lang.String class java.lang.Integer |
总结
- Jackson ObjectMapper 提供了TypeReference支持对泛型对象的反序列化;
- 对于获取泛型类型信息的场景,TypeReference是一个可以参考的通用解决方案。
摘自: