搞定Gson泛型封裝

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

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

有更好的辦法么?當(dāng)然有霉祸!相信也有不少人自己作了嘗試丝蹭,只是有人歡喜有人愁了半夷,不過(guò)沒(méi)關(guān)系迅细,今天我們就來(lái)解決這個(gè)問(wèn)題茵典。

約定

1统阿、本文涉及到的json格式

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

2、假定第一種的對(duì)應(yīng)的Java類(lèi)型為 Result<XXX> 帆离,第二種為 Result<List<XXX>>

一结澄、為何封裝麻献,如何封裝

1们妥、為何封裝:
  • 寫(xiě)new TypeToken<XXX>(){} 麻煩监婶,IDE格式化后還不好看
  • 不同的地方每進(jìn)行一次 new TypeToken<XXX>(){} 操作都會(huì)生成一個(gè)新的類(lèi)
  • 對(duì)于任意類(lèi)XXX都只有兩種情況new TypeToken<Result<XXX>>(){}new TypeToken<Result<List<XXX>>>(){}
  • 方便統(tǒng)一管理
2惑惶、如何封裝

從上面的我們可以知道集惋,最簡(jiǎn)單的方法就是提供兩個(gè)方法分別對(duì)應(yīng)data為Array和Object的情況并接收一個(gè)參數(shù)踩娘,即告知XXX的類(lèi)型养渴,自動(dòng)將完成new TypeToken<XXX>(){}new TypeToken<Result<List<XXX>>>(){}的過(guò)程理卑。

方法原型:

// 處理 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){}

二、為何失敗?

對(duì)于那些嘗試著封裝過(guò)的人可能都這么寫(xiě)過(guò):

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

當(dāng)然上面的寫(xiě)法肯定是沒(méi)有辦法完成的,雖然代碼不會(huì)報(bào)錯(cuò),但運(yùn)行結(jié)果肯定是不對(duì)的自赔,因?yàn)檫@里的T 其實(shí)是一個(gè) TypeVariable绍妨,他在運(yùn)行時(shí)并不會(huì)變成我們想要的XXX柬脸,所以通過(guò)TypeToken 得到的 泛型信息只是 "Result<List<T>>"他去。

三、如何解決?

既然TypeToken的作用是用于獲取泛型的類(lèi)灾测,返回的類(lèi)型為Type行施,真正的泛型信息就是放在這個(gè)Type里面蛾号,既然用TypeToken生成會(huì)有問(wèn)題,那我們自己生成Type就行了嘛涯雅。

Type是Java中所有類(lèi)型的父接口,在1.8以前是一個(gè)空接口活逆,自1.8起多了個(gè)getTypeName()方法蔗候,下面有ParameterizedType锈遥、 GenericArrayType所灸、 WildcardType爬立、 TypeVariable 幾個(gè)接口,以及Class類(lèi)奕巍。這幾個(gè)接口在本次封裝過(guò)程中只會(huì)用到 ParameterizedType ,所以簡(jiǎn)單說(shuō)一下:

ParameterizedType 簡(jiǎn)單說(shuō)來(lái)就是形如“ 類(lèi)型<> ”的類(lèi)型,如:Map<String,User>。下面就以 Map<String,User> 為例講一下里面各個(gè)方法的作用憋沿。

public interface ParameterizedType extends Type {
     // 返回Map<String,User>里的String和User,所以這里返回[String.class,User.clas]
    Type[] getActualTypeArguments(); 
    // Map<String,User>里的Map,所以返回值是Map.class
    Type getRawType();
    // 用于這個(gè)泛型上中包含了內(nèi)部類(lèi)的情況,一般返回null
    Type getOwnerType(); 
}

所以,知道了這里需要的泛型是怎么回事砸民,一切都好說(shuō)了奋救,下面我們來(lái)完成之前留下的空方法岭参。

1、實(shí)現(xiàn)一個(gè)簡(jiǎ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)容可以看出實(shí)際上和Gson關(guān)系不大筐赔,主要的內(nèi)容還是Java的泛型基礎(chǔ)天吓,所以這種封裝的方法同樣適用于其它的框架龄寞。

最后借這次機(jī)會(huì)給安利一個(gè)簡(jiǎn)易的泛型生成庫(kù) TypeBuilder 汰规,其最初實(shí)現(xiàn)的目的就是讓大家快速的生成泛型信息,同時(shí)也會(huì)作一些參數(shù)檢查物邑,保證正確性控轿。

用上面的代碼給大家舉個(gè)例子

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拂封,一起剝皮案震驚了整個(gè)濱河市茬射,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冒签,老刑警劉巖在抛,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異萧恕,居然都是意外死亡刚梭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)票唆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)朴读,“玉大人,你說(shuō)我怎么就攤上這事走趋⌒平穑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)氮唯。 經(jīng)常有香客問(wèn)我鉴吹,道長(zhǎng),這世上最難降的妖魔是什么惩琉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任豆励,我火速辦了婚禮,結(jié)果婚禮上瞒渠,老公的妹妹穿的比我還像新娘良蒸。我一直安慰自己,他們只是感情好伍玖,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布嫩痰。 她就那樣靜靜地躺著,像睡著了一般私沮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上和橙,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天仔燕,我揣著相機(jī)與錄音,去河邊找鬼魔招。 笑死晰搀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的办斑。 我是一名探鬼主播外恕,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼乡翅!你這毒婦竟也來(lái)了鳞疲?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蠕蚜,失蹤者是張志新(化名)和其女友劉穎尚洽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體靶累,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腺毫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了挣柬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潮酒。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖邪蛔,靈堂內(nèi)的尸體忽然破棺而出急黎,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布叁熔,位于F島的核電站委乌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏荣回。R本人自食惡果不足惜遭贸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望心软。 院中可真熱鬧壕吹,春花似錦、人聲如沸删铃。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猎唁。三九已至咒劲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诫隅,已是汗流浹背腐魂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逐纬,地道東北人蛔屹。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像豁生,于是被迫代替她去往敵國(guó)和親兔毒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容