为什么使用TypeReference
在使用fastJson的时候对于泛型的反序列化很多场景下都会使用到TypeReference,例如:
public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); JSONObject o = new JSONObject(); o.put("k",list); List<String> types = o.getObject("k",List.class); System.out.println(JSON.toJSONString(types)); List<String> types2 = o.getObject("k",new TypeReference<List<String>>(){}); System.out.println(JSON.toJSONString(types2)); }
使用TypeReference可以明确的指定反序列化的类型,具体实现逻辑参考TypeReference的构造函数
protected TypeReference(){ Type superClass = getClass().getGenericSuperclass(); Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; Type cachedType = classTypeCache.get(type); if (cachedType == null) { classTypeCache.putIfAbsent(type, type); cachedType = classTypeCache.get(type); } this.type = cachedType; }
核心的方法是getActualTypeArguments
,此方法可以获取父类真实的泛型类型。具体参考注释java.lang.Class#getGenericSuperclass
。new TypeReference<List<String>>(){}
创建了一个继承TypeReference>的匿名子类,在其构造函数中拿到了泛型对应Type(java.lang.reflect.ParameterizedType
)。
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
TypeReference
的存在是因为java中子类可以获取到父类泛型的真实类型,为了便于理解,看一段测试代码
public class TypeReferenceKest { public static void main(String[] args) { IntMap intMap = new IntMap(); System.out.println(intMap.getClass().getSuperclass()); Type type = intMap.getClass().getGenericSuperclass(); if(type instanceof ParameterizedType){ ParameterizedType p = (ParameterizedType) type; for (Type t : p.getActualTypeArguments()){ System.out.println(t); } } System.out.println("=====newclass====="); HashMap<String,Integer> newIntMap = new HashMap<>(); System.out.println(newIntMap.getClass().getSuperclass()); Type newClassType = newIntMap.getClass().getGenericSuperclass(); if(newClassType instanceof ParameterizedType){ ParameterizedType p = (ParameterizedType) newClassType; for (Type t : p.getActualTypeArguments()){ System.out.println(t); } } System.out.println("=====subclass====="); HashMap<String,Integer> subIntMap = new HashMap<String,Integer>(){}; System.out.println(subIntMap.getClass().getSuperclass()); Type subClassType = subIntMap.getClass().getGenericSuperclass(); if(subClassType instanceof ParameterizedType){ ParameterizedType p = (ParameterizedType) subClassType; for (Type t : p.getActualTypeArguments()){ System.out.println(t); } } } public static class IntMap extends HashMap<String,Integer> { } }
输出为
class java.util.HashMap class java.lang.String class java.lang.Integer =====newclass===== class java.util.AbstractMap K V =====subclass===== class java.util.HashMap class java.lang.String class java.lang.Integer
获取到了真实的类型,就可以实现对泛型的反序列化了。
参考资料
http://www.java2s.com/Tutorials/Java/java.lang/Class/Java_Class_getGenericSuperclass_.htm
https://zhaoyanblog.com/archives/186.html
ps.
java虽然运行时会有类型擦除,但是会保留Field的泛型信息,可以通过Field.getGenericType() 取字段的泛型。
exp
public class FieldGenericKest { public Map<String,Integer> map = new HashMap<>(); public List<Long> list = new ArrayList<>(); public static void main(String[] args) throws Exception { FieldGenericKest kest = new FieldGenericKest(); Field map = kest.getClass().getField("map"); Field list = kest.getClass().getField("list"); System.out.println("=====map====="); System.out.println("map.getType=" + map.getType()); System.out.println("map.getGenericType=" + map.getGenericType()); System.out.println("=====list====="); System.out.println("list.getType=" + list.getType()); System.out.println("list.getGenericType=" + list.getGenericType()); } }
输出
=====map===== map.getType=interface java.util.Map map.getGenericType=java.util.Map<java.lang.String, java.lang.Integer> =====list===== list.getType=interface java.util.List list.getGenericType=java.util.List<java.lang.Long>
但是注意,这里不能获取到字段的真实类型HashMap
和ArrayList
。
真实的类型当然不能用Field来获取,需要用对应的Value来获取
Object mapVal = map.get(kest); if(mapVal != null){ Class<?> clz = mapVal.getClass(); System.out.println(mapVal.getClass().getName()); }
pps.
因为泛型的运行时擦除,对于局部变量来说, 泛型信息是无法获取的
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
guava-retrying基于guava的重试模块
简介 The guava-retrying module provides a general purpose method for retrying arbitrary Java code with specific stop, retry, and exception handling capabilities that are enhanced by Guava's predicate matching. maven依赖 <dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency> 使用说明 简单的demo Callable<Boolean> callable = new Callable<Boolean>() { public Boolean call() thro...
- 下一篇
正则表达式(三):python re模块
以下示例所使用 python 版本为: 3.7 python 提供 re 模块,来满足正则表达式的使用。在开始介绍 re 模块之前,首先说明一下两个小内容: 转义字符 \ 转义字符作用是使得字符失去原本的意思,去表示另外一个作用。例如字符 d 表示一个普通的字符 d, 加 \ 转义后 \d 表示数字,字符 s 经转义后,\s 表示空白字符。 如果要匹配转义符号 \ 本身,则需要表达为 \\ 。而在编程语言中要表达两个转义符号 \\,则需要对每个转义符号进行转义,即形式为 \\\\,需要四个转义符号才能完成用于匹配一个转义符号 \ 的正则表达式。 为了减弱转义字符使用上的麻烦,能够将使用者的注意力集中在正则表达式的编写上,这里推荐所有的正则表达式开头使用 r 字符开头,表示正则内容作为原始字符串输入到 re 模块的使用中。即编程环境中 r'\\' 直接作为正则表达式使用,来完成对字符 \ 的匹配。 示例: import re reg = r'\\' str = '\\' #转义符号,str实际值为\ if re.match(reg,str): print('match {}'.forma...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装