欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Gson中的TypeToken與泛型擦除詳情

 更新時(shí)間:2022年09月06日 08:32:15   作者:扣釘日記  
這篇文章主要介紹了Gson中的TypeToken與泛型擦除詳情,其Gson類(lèi)提供了toJson()與fromJson()方法,分別用來(lái)序列化與反序列化,更多相關(guān)內(nèi)容需要的朋友可以參考一下

問(wèn)題

在Java的json框架中,Gson是使用得比較廣泛的一個(gè),其Gson類(lèi)提供了toJson()fromJson()方法,分別用來(lái)序列化與反序列化。

json序列化用得最多的場(chǎng)景是在調(diào)用外部服務(wù)接口時(shí),大致如下:

@Data
@AllArgsConstructor
public class Response<T>{
    int code;
    String message;
    T body;
}

@Data
@AllArgsConstructor
public class PersonInfo{
    long id;
    String name;
    int age;
}

/**
 * 服務(wù)端
 */
public class Server {
    public static String getPersonById(Long id){
        PersonInfo personInfo = new PersonInfo(1234L, "zhangesan", 18);
        Response<PersonInfo> response = new Response<>(200, "success", personInfo);
        //序列化
        return new Gson().toJson(response);
    }
}

/**
 * 客戶(hù)端
 */
public class Client {
    public static void getPerson(){
        String responseStr = Server.getPersonById(1234L);
        //反序列化
        Response<PersonInfo> response = new Gson().fromJson(responseStr, new TypeToken<Response<PersonInfo>>(){}.getType());
        System.out.println(response);
    }
}

由于大多數(shù)接口設(shè)計(jì)中,都會(huì)有統(tǒng)一的響應(yīng)碼結(jié)構(gòu),因此大多項(xiàng)目都會(huì)像上面一樣,設(shè)計(jì)一個(gè)通用Response類(lèi)來(lái)對(duì)應(yīng)這種統(tǒng)一響應(yīng)碼結(jié)構(gòu),是很常見(jiàn)的情況。

但會(huì)發(fā)現(xiàn),在反序列化過(guò)程中,傳入目標(biāo)類(lèi)型時(shí),使用了一段很奇怪的代碼,即new TypeToken<Response<PersonInfo>>(){}.getType(),那它是什么?為啥要使用它?

TypeToken是什么

為什么要使用TypeToken呢?我們直接使用Response<PersonInfo>.class行不行?如下:

可以發(fā)現(xiàn),java并不允許這么使用!

那傳Response.class呢?如下:

可以發(fā)現(xiàn),代碼能跑起來(lái),但是Body變成了LinkedHashMap類(lèi)型,這是因?yàn)閭鹘ogson的類(lèi)型是Response.class,gson并不知道body屬性是什么類(lèi)型,那它只能使用LinkedHashMap這個(gè)默認(rèn)的json對(duì)象類(lèi)型了。

這就是TypeToken由來(lái)的原因,對(duì)于帶泛型的類(lèi),使用TypeToken才能得到準(zhǔn)確的類(lèi)型信息,那TypeToken是怎么取到準(zhǔn)確的類(lèi)型的呢?

首先,new TypeToken<Response<PersonInfo>>(){}.getType()實(shí)際上是定義了一個(gè)匿名內(nèi)部類(lèi)的對(duì)象,然后調(diào)用了這個(gè)對(duì)象的getType()方法。

看看getType()的實(shí)現(xiàn),如下:

邏輯也比較簡(jiǎn)單,先通過(guò)getGenericSuperclass()獲取了此對(duì)象的父類(lèi),即TypeToken<Response<PersonInfo>>,然后又通過(guò)getActualTypeArguments()[0]獲取了實(shí)際類(lèi)型參數(shù),即Response<PersonInfo>

額,邏輯看起來(lái)說(shuō)得通,但不是說(shuō)Java泛型會(huì)擦除嗎?這里不會(huì)擦除?

從所周知,java泛型擦除發(fā)生在編譯期,ok,那我模擬上面的原理,寫(xiě)個(gè)空類(lèi)繼承TypeToken<Response<PersonInfo>>,然后編譯這個(gè)類(lèi)之后再反編譯一下,看類(lèi)型到底擦除沒(méi)!

public class PersonResponseTypeToken extends TypeToken<Response<PersonInfo>> {

}

反編譯結(jié)果如下:

也就是說(shuō),被繼承的父類(lèi)上的泛型是不擦除的。

其它使用場(chǎng)景

有時(shí)為了編程的方便,經(jīng)常會(huì)有框架將遠(yuǎn)程調(diào)用接口化,類(lèi)似下面這樣:

public class RemoteUtil {
    private static final ConcurrentMap<Class, Object> REMOTE_CACHE = new ConcurrentHashMap<>();

    public static <T> T get(Class<T> clazz) {
        return clazz.cast(REMOTE_CACHE.computeIfAbsent(clazz, RemoteUtil::getProxyInstance));
    }

    private static Object getProxyInstance(Class clazz) {
        return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{clazz}, (proxy, method, args) -> {
            Gson gson = new Gson();
            String path = method.getAnnotation(RequestMapping.class).path()[0];
            HttpURLConnection conn = null;
            try {
                conn = (HttpURLConnection) new URL("http://localhost:8080/" + path).openConnection();
                conn.setRequestMethod("POST");
                conn.setDoOutput(true);
                conn.setDoInput(true);
                conn.connect();
                //設(shè)置請(qǐng)求數(shù)據(jù)
                JsonObject requestBody = new JsonObject();
                try (Writer out = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8)) {
                    int i = 0;
                    for (Parameter parameter : method.getParameters()) {
                        String name = parameter.getAnnotation(RequestParam.class).name();
                        requestBody.add(name, gson.toJsonTree(args[i]));
                        i++;
                    }
                    out.write(requestBody.toString());
                }
                //獲取響應(yīng)數(shù)據(jù)
                if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                    throw new RuntimeException("遠(yuǎn)程調(diào)用發(fā)生異常:url:" + conn.getURL() + ", requestBody:" + requestBody);
                }
                String responseStr = IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8);
                //響應(yīng)結(jié)果反序列化為具體對(duì)象
                return gson.fromJson(responseStr, method.getReturnType());
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
            }
        });
    }

}

public interface PersonApi {
    @RequestMapping(path = "/person")
    Response<PersonInfo> getPersonById(@RequestParam(name = "id") Long id);
}

public class Client {

    public static void getPerson() {
        Response<PersonInfo> response = RemoteUtil.get(PersonApi.class).getPersonById(1234L);
        System.out.println(response.getBody());
    }
}

這樣做的好處是,開(kāi)發(fā)人員不必再關(guān)心如何發(fā)遠(yuǎn)程請(qǐng)求了,只需要定義與調(diào)用接口即可。

但上面調(diào)用過(guò)程中會(huì)有一個(gè)問(wèn)題,就是獲取的response對(duì)象中body屬性是LinkedHashMap,原因是gson反序列化時(shí)是通過(guò)method.getReturnType()來(lái)獲取返回類(lèi)型的,而返回類(lèi)型中的泛型會(huì)被擦除掉。

要解決這個(gè)問(wèn)題也很簡(jiǎn)單,和上面TypeToken一樣的道理,定義一個(gè)空類(lèi)PersonResponse來(lái)繼承Response<PersonInfo>,然后將返回類(lèi)型定義為PersonResponse

如下:

public class PersonResponse extends Response<PersonInfo> {
}

public interface PersonApi {
    @RequestMapping(path = "/person")
    PersonResponse getPersonById(@RequestParam(name = "id") Long id);
}

然后你就會(huì)發(fā)現(xiàn),gson可以正確識(shí)別到body屬性的類(lèi)型了。

到此這篇關(guān)于Gson中的TypeToken與泛型擦除詳情的文章就介紹到這了,更多相關(guān)Gson TypeToken內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring實(shí)現(xiàn)泛型注入的示例詳解

    Spring實(shí)現(xiàn)泛型注入的示例詳解

    Spring?4.0版本中更新了很多新功能,其中比較重要的一個(gè)就是對(duì)帶泛型的Bean進(jìn)行依賴(lài)注入的支持。本文將通過(guò)實(shí)例詳細(xì)講講Spring如何實(shí)現(xiàn)泛型注入,需要的可以參考一下
    2022-07-07
  • Java實(shí)現(xiàn)聊天機(jī)器人完善版

    Java實(shí)現(xiàn)聊天機(jī)器人完善版

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)聊天機(jī)器人完善版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 關(guān)于JwtToken使用-重點(diǎn)看一下過(guò)期時(shí)間

    關(guān)于JwtToken使用-重點(diǎn)看一下過(guò)期時(shí)間

    這篇文章主要介紹了關(guān)于JwtToken使用-重點(diǎn)看一下過(guò)期時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Java輸入/輸出流體系詳解

    Java輸入/輸出流體系詳解

    這篇文章主要介紹了Java輸入/輸出流體系詳解,涉及字節(jié)流和字符流,輸入輸出體系,轉(zhuǎn)換流,以及文件的讀寫(xiě)等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Java實(shí)現(xiàn)訂單超時(shí)未支付自動(dòng)取消的8種方法總結(jié)

    Java實(shí)現(xiàn)訂單超時(shí)未支付自動(dòng)取消的8種方法總結(jié)

    這篇文章主要為大家介紹了Java實(shí)現(xiàn)訂單超時(shí)未支付自動(dòng)取消功能的8種不同方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-08-08
  • 詳解IntelliJ IDEA 自定義方法注解模板

    詳解IntelliJ IDEA 自定義方法注解模板

    本篇文章主要介紹了IntelliJ IDEA 自定義方法注解模板,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • redisson特性及優(yōu)雅實(shí)現(xiàn)示例

    redisson特性及優(yōu)雅實(shí)現(xiàn)示例

    這篇文章主要為大家介紹了redisson特性及優(yōu)雅實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Java Set集合的遍歷及實(shí)現(xiàn)類(lèi)的比較

    Java Set集合的遍歷及實(shí)現(xiàn)類(lèi)的比較

    這篇文章主要介紹了Java Set集合的遍歷及實(shí)現(xiàn)類(lèi)的比較的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Spring中的@PostConstruct注解使用方法解析

    Spring中的@PostConstruct注解使用方法解析

    這篇文章主要介紹了Spring中的@PostConstruct注解使用方法解析,@PostConstruct注解是用來(lái)處理在@Autowired注入屬性后init()方法之前,對(duì)一些零散的屬性進(jìn)行賦值的注解,需要的朋友可以參考下
    2023-11-11
  • SpringBoot v2.2以上重復(fù)讀取Request Body內(nèi)容的解決方案

    SpringBoot v2.2以上重復(fù)讀取Request Body內(nèi)容的解決方案

    這篇文章主要介紹了SpringBoot v2.2以上重復(fù)讀取Request Body內(nèi)容的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評(píng)論