zoukankan      html  css  js  c++  java
  • [转]搞定Gson泛型封装

    作者: @怪盗kidou
    如需转载需在明显位置保留作者信息及原文链接
    如果博客中有不恰当之处欢迎留言交流
    http://www.jianshu.com/p/d62c2be60617

    在你真的会用Gson吗?Gson使用指南(一) 的第三节我介绍了在Gson中如何使用泛型来简化我们的类设计,但随之而来引入了一个新的问题:封装。不知道各位有没有想过这样一个问题:每次都要用 new TypeToken<XXX>(){}; 好麻烦,有没有更好的办法?

    有更好的办法么?当然有!相信也有不少人自己作了尝试,只是有人欢喜有人愁了,不过没关系,今天我们就来解决这个问题。

    约定

    1、本文涉及到的json格式

    // data 为 object 的情况
    {"code":"0","message":"success","data":{}}
    // data 为 array 的情况
    {"code":"0","message":"success","data":[]}
    

    2、假定第一种的对应的Java类型为 Result<XXX> ,第二种为 Result<List<XXX>>

    一、为何封装,如何封装

    1、为何封装:

    • new TypeToken<XXX>(){} 麻烦,IDE格式化后还不好看
    • 不同的地方每进行一次 new TypeToken<XXX>(){} 操作都会生成一个新的类
    • 对于任意类XXX都只有两种情况new TypeToken<Result<XXX>>(){}new TypeToken<Result<List<XXX>>>(){}
    • 方便统一管理

    2、如何封装

    从上面的我们可以知道,最简单的方法就是提供两个方法分别对应data为Array和Object的情况并接收一个参数,即告知XXX的类型,自动将完成new TypeToken<XXX>(){}new TypeToken<Result<List<XXX>>>(){}的过程。

    方法原型:

    // 处理 data 为 object 的情况
    public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {}
    // 处理 data 为 array 的情况
    public static <T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz){}
    

    二、为何失败?

    对于那些尝试着封装过的人可能都这么写过:

    public static <T> Result<List<T>> fromJsonArray(Reader reader) {
        Type type = new TypeToken<Result<List<T>>>(){}.getType();
        return GSON.fromJson(reader, type);
    }
    

    当然上面的写法肯定是没有办法完成的,虽然代码不会报错,但运行结果肯定是不对的,因为这里的T 其实是一个 TypeVariable,他在运行时并不会变成我们想要的XXX,所以通过TypeToken 得到的 泛型信息只是 "Result<List<T>>"

    三、如何解决?

    既然TypeToken的作用是用于获取泛型的类,返回的类型为Type,真正的泛型信息就是放在这个Type里面,既然用TypeToken生成会有问题,那我们自己生成Type就行了嘛。

    Type是Java中所有类型的父接口,在1.8以前是一个空接口,自1.8起多了个getTypeName()方法,下面有ParameterizedType、 GenericArrayType、 WildcardType、 TypeVariable 几个接口,以及Class类。这几个接口在本次封装过程中只会用到 ParameterizedType ,所以简单说一下:

    ParameterizedType 简单说来就是形如 ** 类型<> ** 的类型,如:Map<String,User>。下面就以 Map<String,User> 为例讲一下里面各个方法的作用。

    public interface ParameterizedType extends Type {
         // 返回Map<String,User>里的String和User,所以这里返回[String.class,User.clas]
        Type[] getActualTypeArguments(); 
        // Map<String,User>里的Map,所以返回值是Map.class
        Type getRawType();
        // 用于这个泛型上中包含了内部类的情况,一般返回null
        Type getOwnerType(); 
    }
    

    所以,知道了这里需要的泛型是怎么回事,一切都好说了,下面我们来完成之前留下的空方法。

    1、实现一个简易的 ParameterizedType

    public class ParameterizedTypeImpl implements ParameterizedType {
        private final Class raw;
        private final Type[] args;
        public ParameterizedTypeImpl(Class raw, Type[] args) {
            this.raw = raw;
            this.args = args != null ? args : new Type[0];
        }
        @Override
        public Type[] getActualTypeArguments() {
            return args;
        }
        @Override
        public Type getRawType() {
            return raw;
        }
        @Override
        public Type getOwnerType() {return null;}
    }
    

    2、生成Gson需要的泛型

    2.1解析data是object的情况

    public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {
        Type type = new ParameterizedTypeImpl(Result.class, new Class[]{clazz});
        return GSON.fromJson(reader, type);
    }
    

    2.2解析data是array的情况

    是Array的情况要比是Object的情况多那么一步。

    public static <T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz) {
        // 生成List<T> 中的 List<T>
        Type listType = new ParameterizedTypeImpl(List.class, new Class[]{clazz});
        // 根据List<T>生成完整的Result<List<T>>
        Type type = new ParameterizedTypeImpl(Result.class, new Type[]{listType});
        return GSON.fromJson(reader, type);
    }
    

    本次代码较少,不提供源码

    虽然这篇博客是以Gson为例,但从上面的内容可以看出实际上和Gson关系不大,主要的内容还是Java的泛型基础,所以这种封装的方法同样适用于其它的框架。

    最后借这次机会给安利一个简易的泛型生成库 TypeBuilder ,其最初实现的目的就是让大家快速的生成泛型信息,同时也会作一些参数检查,保证正确性。

    用上面的代码给大家举个例子

    public static <T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz) {
        Type type = TypeBuilder
                .newInstance(Result.class)
                .beginSubType(List.class)
                .addTypeParam(clazz)
                .endSubType()
                .build();
        return GSON.fromJson(reader, type);
    }
    
    public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {
        Type type = TypeBuilder
                .newInstance(Result.class)
                .addTypeParam(clazz)
                .build();
        return GSON.fromJson(reader, type);
    }
    
  • 相关阅读:
    Leetcode 191.位1的个数 By Python
    反向传播的推导
    Leetcode 268.缺失数字 By Python
    Leetcode 326.3的幂 By Python
    Leetcode 28.实现strStr() By Python
    Leetcode 7.反转整数 By Python
    Leetcode 125.验证回文串 By Python
    Leetcode 1.两数之和 By Python
    Hdoj 1008.Elevator 题解
    TZOJ 车辆拥挤相互往里走
  • 原文地址:https://www.cnblogs.com/ElEGenT/p/13047753.html
Copyright © 2011-2022 走看看