为什么使用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>

但是注意,这里不能获取到字段的真实类型HashMapArrayList
真实的类型当然不能用Field来获取,需要用对应的Value来获取

Object mapVal = map.get(kest);
if(mapVal != null){
    Class<?> clz = mapVal.getClass();
    System.out.println(mapVal.getClass().getName());
}

pps.
因为泛型的运行时擦除,对于局部变量来说, 泛型信息是无法获取的

目录
相关文章
|
Java Spring
Spring AOP切点表达式(Pointcut)详解
Spring 的 AOP 中的一个核心概念是切点(Pointcut),切点表达式定义通知(Advice)执行的范围。
5136 0
|
JSON 前端开发 Java
RestfulToolKit:便捷的 IDEA 接口测试神器
RestfulToolKit:便捷的 IDEA 接口测试神器
2450 0
RestfulToolKit:便捷的 IDEA 接口测试神器
|
JSON fastjson Java
Fastjson 序列化,反序列化Map对象排序问题(字符串转map,map转字符串)
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qingfeng812/article/details/61194594 ...
5122 0
|
缓存 Java 程序员
Spring中异步注解@Async的使用、原理及使用时可能导致的问题
本文主要介绍了Spring中异步注解的使用、原理及可能碰到的问题,针对每个问题文中也给出了方案。希望通过这篇文章能帮助你彻底掌握`@Async`注解的使用,知其然并知其所以然!
14245 4
IDEA 自定义注解(类注释、方法注释)
IDEA 自定义注解(类注释、方法注释)
6009 1
IDEA 自定义注解(类注释、方法注释)
|
JSON Java fastjson
JMH - Java 代码性能测试的终极利器、必须掌握
JMH - Java 代码性能测试的终极利器、必须掌握
4342 1
一起来学kafka之整合SpringBoot深入使用(一)
前言 目前正在出一个Kafka专题系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~ 本节给大家讲一下Kafka整合SpringBoot中如何进行消息应答以及@SendTo 和 @KafkaListener的讲解~ 好了, 废话不多说直接开整吧~ 消息应答 有时候,消费者消费消息的时候,我们需要知道它有没有消费完,需要它给我们一个回应,该怎么做呢? 我们可以通过提供的ReplyingKafkaTemplate, 下面通过一个例子来体验一下,新建一个ReceiveCustomerController
|
存储 消息中间件 缓存
本地缓存之王,Caffeine保姆级教程
本地缓存之王,Caffeine保姆级教程
|
存储 JSON 缓存
看看人家在接口中使用枚举类型的方式,那叫一个优雅!上
看看人家在接口中使用枚举类型的方式,那叫一个优雅!上
看看人家在接口中使用枚举类型的方式,那叫一个优雅!上
|
数据采集 XML 前端开发
Spring MVC(spring-webmvc)之全局数据处理、拦截器、自定义类型转换器等使用指南
Spring MVC(spring-webmvc)之全局数据处理、拦截器、自定义类型转换器等使用指南
470 0
Spring MVC(spring-webmvc)之全局数据处理、拦截器、自定义类型转换器等使用指南