最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Gson中的TypeToken與泛型擦除詳情

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

問題

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

json序列化用得最多的場景是在調用外部服務接口時,大致如下:

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

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

/**
 * 服務端
 */
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);
    }
}

/**
 * 客戶端
 */
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ù)接口設計中,都會有統(tǒng)一的響應碼結構,因此大多項目都會像上面一樣,設計一個通用Response類來對應這種統(tǒng)一響應碼結構,是很常見的情況。

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

TypeToken是什么

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

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

那傳Response.class呢?如下:

可以發(fā)現(xiàn),代碼能跑起來,但是Body變成了LinkedHashMap類型,這是因為傳給gson的類型是Response.class,gson并不知道body屬性是什么類型,那它只能使用LinkedHashMap這個默認的json對象類型了。

這就是TypeToken由來的原因,對于帶泛型的類,使用TypeToken才能得到準確的類型信息,那TypeToken是怎么取到準確的類型的呢?

首先,new TypeToken<Response<PersonInfo>>(){}.getType()實際上是定義了一個匿名內部類的對象,然后調用了這個對象的getType()方法。

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

邏輯也比較簡單,先通過getGenericSuperclass()獲取了此對象的父類,即TypeToken<Response<PersonInfo>>,然后又通過getActualTypeArguments()[0]獲取了實際類型參數(shù),即Response<PersonInfo>

額,邏輯看起來說得通,但不是說Java泛型會擦除嗎?這里不會擦除?

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

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

}

反編譯結果如下:

也就是說,被繼承的父類上的泛型是不擦除的。

其它使用場景

有時為了編程的方便,經常會有框架將遠程調用接口化,類似下面這樣:

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ù)據(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());
                }
                //獲取響應數(shù)據(jù)
                if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                    throw new RuntimeException("遠程調用發(fā)生異常:url:" + conn.getURL() + ", requestBody:" + requestBody);
                }
                String responseStr = IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8);
                //響應結果反序列化為具體對象
                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());
    }
}

這樣做的好處是,開發(fā)人員不必再關心如何發(fā)遠程請求了,只需要定義與調用接口即可。

但上面調用過程中會有一個問題,就是獲取的response對象中body屬性是LinkedHashMap,原因是gson反序列化時是通過method.getReturnType()來獲取返回類型的,而返回類型中的泛型會被擦除掉。

要解決這個問題也很簡單,和上面TypeToken一樣的道理,定義一個空類PersonResponse來繼承Response<PersonInfo>,然后將返回類型定義為PersonResponse

如下:

public class PersonResponse extends Response<PersonInfo> {
}

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

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

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

相關文章

  • SpringBoot使用注解進行分頁的實現(xiàn)示例

    SpringBoot使用注解進行分頁的實現(xiàn)示例

    分頁使用可以說非常普遍了,有時候會需要非常靈活的方式去開啟或關閉分頁,嘗試使用一下注解的方式來進行分頁,本文主要介紹了SpringBoot使用注解進行分頁的實現(xiàn)示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Java?數(shù)據(jù)庫連接池Druid?的介紹

    Java?數(shù)據(jù)庫連接池Druid?的介紹

    這篇文章主要給大家分享的是?Java?數(shù)據(jù)庫連接池Druid?的介紹,Druid是一個JDBC組件,它包括三部分:?DruidDriver?代理Driver,能夠提供基于Filter-Chain模式的插件體系。?DruidDataSource?高效可管理的數(shù)據(jù)庫連接池,下面來看看文中的詳細內容,需要的朋友也可以參考一下
    2021-11-11
  • WPF實現(xiàn)自定義一個自刪除的多功能ListBox

    WPF實現(xiàn)自定義一個自刪除的多功能ListBox

    這篇文章主要為大家詳細介紹了如何利用WPF實現(xiàn)自定義一個自刪除的多功能ListBox,文中示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2022-12-12
  • java 自己實現(xiàn)DataSource實現(xiàn)實例

    java 自己實現(xiàn)DataSource實現(xiàn)實例

    這篇文章主要介紹了java 自己實現(xiàn)DataSource實現(xiàn)代碼的相關資料,需要的朋友可以參考下
    2017-05-05
  • Java設計實現(xiàn)一個針對各種類型的緩存

    Java設計實現(xiàn)一個針對各種類型的緩存

    這篇文章主要為大家詳細介紹了Java如何設計實現(xiàn)一個針對各種類型的緩存,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以了解一下
    2023-11-11
  • java實現(xiàn)文件上傳到服務器

    java實現(xiàn)文件上傳到服務器

    這篇文章主要為大家詳細介紹了java實現(xiàn)文件上傳到服務器,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Spring Boot和Vue跨域請求問題原理解析

    Spring Boot和Vue跨域請求問題原理解析

    這篇文章主要介紹了Spring Boot和Vue跨域請求問題原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-12-12
  • java對ArrayList排序代碼示例

    java對ArrayList排序代碼示例

    本文通過代碼示例給大家介紹java對arraylist排序,代碼簡潔易懂,感興趣的朋友一起學習吧
    2015-11-11
  • Spring AOP的底層實現(xiàn)方式-代理模式

    Spring AOP的底層實現(xiàn)方式-代理模式

    這篇文章主要介紹了Spring AOP的底層實現(xiàn)方式-代理模式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Netty分布式ByteBuf使用的回收邏輯剖析

    Netty分布式ByteBuf使用的回收邏輯剖析

    這篇文章主要介紹了Netty分布式ByteBuf使用的回收邏輯剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-03-03

最新評論

长葛市| 成安县| 正宁县| 含山县| 中宁县| 晋城| 长春市| 年辖:市辖区| 鄂托克旗| 高雄县| 穆棱市| 苏州市| 阿尔山市| 原阳县| 伊宁县| 名山县| 遵义市| 平阴县| 色达县| 邳州市| 玉门市| 噶尔县| 秦安县| 阳山县| 台北县| 徐水县| 息烽县| 杭锦后旗| 杭锦后旗| 江西省| 苗栗县| 白玉县| 通许县| 盖州市| 浦城县| 抚顺县| 新闻| 察哈| 镇沅| 永州市| 屯门区|