搞定Gson泛型封裝

作者: @怪盜kidou
如需轉(zhuǎn)載需在明顯位置保留作者信息及原文鏈接
如果博客中有不恰當(dāng)之處歡迎留言交流
http://www.itdecent.cn/p/d62c2be60617

你真的會用Gson嗎?Gson使用指南(一) 的第三節(jié)我介紹了在Gson中如何使用泛型來簡化我們的類設(shè)計,但隨之而來引入了一個新的問題:封裝。不知道各位有沒有想過這樣一個問題:每次都要用 new TypeToken<XXX>(){}; 好麻煩,有沒有更好的辦法?

有更好的辦法么?當(dāng)然有!相信也有不少人自己作了嘗試,只是有人歡喜有人愁了,不過沒關(guān)系,今天我們就來解決這個問題。

約定

1、本文涉及到的json格式

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

2、假定第一種的對應(yīng)的Java類型為 Result<XXX> ,第二種為 Result<List<XXX>>

一、為何封裝,如何封裝

1、為何封裝:
  • new TypeToken<XXX>(){} 麻煩,IDE格式化后還不好看
  • 不同的地方每進行一次 new TypeToken<XXX>(){} 操作都會生成一個新的類
  • 對于任意類XXX都只有兩種情況new TypeToken<Result<XXX>>(){}new TypeToken<Result<List<XXX>>>(){}
  • 方便統(tǒng)一管理
2、如何封裝

從上面的我們可以知道,最簡單的方法就是提供兩個方法分別對應(yīng)data為Array和Object的情況并接收一個參數(shù),即告知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);
}

當(dāng)然上面的寫法肯定是沒有辦法完成的,雖然代碼不會報錯,但運行結(jié)果肯定是不對的,因為這里的T 其實是一個 TypeVariable,他在運行時并不會變成我們想要的XXX,所以通過TypeToken 得到的 泛型信息只是 "Result<List<T>>"

三、如何解決?

既然TypeToken的作用是用于獲取泛型的類,返回的類型為Type,真正的泛型信息就是放在這個Type里面,既然用TypeToken生成會有問題,那我們自己生成Type就行了嘛。

Type是Java中所有類型的父接口,在1.8以前是一個空接口,自1.8起多了個getTypeName()方法,下面有ParameterizedTypeGenericArrayType、 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();
    // 用于這個泛型上中包含了內(nèi)部類的情況,一般返回null
    Type getOwnerType(); 
}

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

1、實現(xiàn)一個簡易的 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});
    // 根據(jù)List<T>生成完整的Result<List<T>>
    Type type = new ParameterizedTypeImpl(Result.class, new Type[]{listType});
    return GSON.fromJson(reader, type);
}

本次代碼較少,不提供源碼

雖然這篇博客是以Gson為例,但從上面的內(nèi)容可以看出實際上和Gson關(guān)系不大,主要的內(nèi)容還是Java的泛型基礎(chǔ),所以這種封裝的方法同樣適用于其它的框架。

最后借這次機會給安利一個簡易的泛型生成庫 TypeBuilder ,其最初實現(xiàn)的目的就是讓大家快速的生成泛型信息,同時也會作一些參數(shù)檢查,保證正確性。

用上面的代碼給大家舉個例子

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);
}

我最近剛剛開通了微信公眾號(怪盜kidou),歡迎關(guān)注

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容