摘要:回到的第二方法的用法,通過上面的分析,我們可以知道,方法其實也是用來獲取泛型的實際類型的,這樣就可以將響應(yīng)反序列化為帶泛型的類型了。在很多反序列化的開源組件中,都用了這個原理例如的方法,所以我們會經(jīng)常見到實例化的時候會多個花括號。
前段日子在使用google-http-client.jar 這個組件做http請求時,發(fā)現(xiàn)一件有趣的事情,具體代碼如下:
try { HttpTransport transport = new NetHttpTransport.Builder().doNotValidateCertificate().build(); requestFactory = transport.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) { int timeout = 5 * 1000; request.setReadTimeout(timeout); request.setParser(new JsonObjectParser(new JacksonFactory())); request.setThrowExceptionOnExecuteError(false); logger.debug("set timeout = {} milliseconds", timeout); } }); } catch (GeneralSecurityException e) { logger.error("init static members failed:", e); } HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(url), content); HttpResponse response =request.execute(); Bean ret = (Map)response.parseAs(Bean.class); ......
這是一段很簡單的http請求的代碼,引起我注意的是最后一段代碼,并且有個疑問:
為什么HttpResponse.parseAs方法可以通過入?yún)ean.class就能夠?qū)⒔Y(jié)果裝配到Bean類,并返回Bean類型?
事實上,HttpResponse.parseAs有兩個同名的重載方法:
publicT parseAs(Class dataClass) throws IOException { if (!hasMessageBody()) { return null; } return request.getParser().parseAndClose(getContent(), getContentCharset(), dataClass); } public Object parseAs(Type dataType) throws IOException { if (!hasMessageBody()) { return null; } return request.getParser().parseAndClose(getContent(), getContentCharset(), dataType); }
兩個入?yún)⒉煌?,返回的類型也不同,第一個方法可以在編譯期返回確切的類型,第二個只能返回Object類型,需要使用者自行強轉(zhuǎn)。那么這兩個方法到底有什么區(qū)別呢,既然存在肯定是為了解決什么問題吧。我們來看看這兩個方法用在哪兒:
1、Bean ret = response.parseAs(Bean.class); 2、Mapret = (Map )response.parseAs(new TypeToken
相信已經(jīng)有的朋友已經(jīng)看出來了, 像Map
package org.hxb.spring.generic; import java.lang.reflect.ParameterizedType; import java.util.Arrays; import java.util.Map; import org.junit.Test; public class GenericTest { @Test public void test1() { Bean
輸出: [T] [java.util.Map]
有人會問我,為什么Bean
Bean> a = new Bean >(); Bean > a = new Bean >(){};
下面那句話多了一對花括號,相信大家都知道這是什么意思,這樣就創(chuàng)建了一個匿名類,
第一種方法顯示a的類型是Bean
第一種方法顯示a的類型是GenericTest$1
匿名類繼承類型Bean
所以getGenericSuperclass方法返回一個ParameterizedType的結(jié)果,然后通過ParameterizedType的getActualTypeArguments方法便可以獲取實際的類型,實際上用這種方法的話Bean就無需在編譯器繼承某個父類了,直接在運行時聲明一個匿名類即可:
package org.hxb.spring.generic; import java.lang.reflect.ParameterizedType; import java.util.Arrays; import java.util.Map; import org.junit.Test; public class GenericTest { @Test public void test2() { Bean> a = new Bean >() { }; ParameterizedType type = (ParameterizedType) a.getClass().getGenericSuperclass(); if (type.getActualTypeArguments() != null) { System.out.println(Arrays.asList(type.getActualTypeArguments())); } } } class Bean { }
上述代碼亦可以輸出實際類型。
回到HttpResponse的第二parseAs方法的用法:Map
package org.hxb.spring.generic; import java.lang.reflect.ParameterizedType; import java.util.Map; import org.junit.Test; import com.google.common.reflect.TypeToken; public class GenericTest { @Test public void test2() { Bean> a = new Bean >() { }; ParameterizedType type = (ParameterizedType) a.getClass().getGenericSuperclass(); if (type.getActualTypeArguments() != null) { System.out.println(type.getActualTypeArguments()[0]); } } @Test public void test3() { System.out.println(new TypeToken >() {}.getType()); } } class Bean { }
實際輸出:
實驗結(jié)果和我們猜想的那樣,我們再看看TypeToken的無參構(gòu)造方法,
無參構(gòu)造方法的訪問權(quán)限是protected,有人會問了,那我怎么實例化?呵呵,其實作者的意圖就是為了確保你不能直接實例化TypeToken,但是我們可以用匿名實現(xiàn)類直接繼承TypeToken并實例化(就是多了對花括號{})。
無參構(gòu)造方法調(diào)用了父類的capture(捕獲)方法,從截圖中可以看到,該方法調(diào)用了getGenericSuperclass,返回并且判斷父類的類型是不是ParameterizedType,不是的話便拋出異常,是就返回第一個。這也驗證了我們的想法,其實parseAs方法就是用了上面的原理。
在很多反序列化的開源組件中,都用了這個原理例如com.fasterxml.jackson.databind.ObjectMapper.ObjectMapper 的readValue方法,所以我們會經(jīng)常見到實例化的時候會多個花括號。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/68802.html
類型擦除 泛型被引入到Java語言中,以便在編譯時提供更嚴(yán)格的類型檢查并支持通用編程,為了實現(xiàn)泛型,Java編譯器將類型擦除應(yīng)用于: 如果類型參數(shù)是無界的,則用它們的邊界或Object替換泛型類型中的所有類型參數(shù),因此,生成的字節(jié)碼僅包含普通的類、接口和方法。 如有必要,插入類型轉(zhuǎn)換以保持類型安全。 生成橋接方法以保留擴展泛型類型中的多態(tài)性。 類型擦除確保不為參數(shù)化類型創(chuàng)建新類,因此,泛型不會...
摘要:知識點總結(jié)泛型知識點總結(jié)泛型泛型泛型就是參數(shù)化類型適用于多種數(shù)據(jù)類型執(zhí)行相同的代碼泛型中的類型在使用時指定泛型歸根到底就是模版優(yōu)點使用泛型時,在實際使用之前類型就已經(jīng)確定了,不需要強制類型轉(zhuǎn)換。 Java知識點總結(jié)(Java泛型) @(Java知識點總結(jié))[Java, Java泛型] [toc] 泛型 泛型就是參數(shù)化類型 適用于多種數(shù)據(jù)類型執(zhí)行相同的代碼 泛型中的類型在使用時指定 泛...
摘要:知識點總結(jié)反射反射操作泛型知識點總結(jié)反射采用泛型擦除的機制來引入泛型。中的泛型僅僅是給編譯器使用的,確保數(shù)據(jù)的安全性和免去強制類型轉(zhuǎn)換的麻煩。 Java知識點總結(jié)(反射-反射操作泛型) @(Java知識點總結(jié))[Java, 反射] Java采用泛型擦除的機制來引入泛型。Java中的泛型僅僅是給編譯器javac使用的, 確保數(shù)據(jù)的安全性和免去強制類型轉(zhuǎn)換的麻煩 。但是,__一旦編譯完成,...
閱讀 1813·2023-04-26 02:14
閱讀 3738·2021-11-23 09:51
閱讀 1390·2021-10-13 09:39
閱讀 3980·2021-09-24 10:36
閱讀 3020·2021-09-22 15:55
閱讀 3524·2019-08-30 12:57
閱讀 2044·2019-08-29 15:30
閱讀 1988·2019-08-29 13:19