Java的泛型擦除和運行時泛型信息獲取
只要理解了 Java 引入泛型擦除的原因,也自然能理解如何在運行時獲取泛型信息了。下面是小編為大家帶來的Java的泛型擦除和運行時泛型信息獲取的知識,歡迎閲讀。
Java 的泛型擦除
程序員界有句流行的話,叫 talk is cheap, show me the code,所以話不多説,看代碼。
代碼一
Java代碼
Class c1 = new ArrayList()lass();
Class c2 = new ArrayList()lass();
tln(c1 == c2);
/* Output
true
*/
ArrayList 和 ArrayList 在編譯的時候是完全不同的類型。你無法在寫代碼時,把一個 String 類型的實例加到 ArrayList 中。但是在程序運行時,的的確確會輸出true。
這就是 Java 泛型的類型擦除造成的,因為不管是 ArrayList 還是 ArrayList,在編譯時都會被編譯器擦除成了 ArrayList。Java 之所以要避免在創建泛型實例時而創建新的類,從而避免運行時的過度消耗。
代碼二
Java代碼
List list = new ArrayList();
Map map = new HashMap();
tln(ring(lass()ypeParameters()));
tln(ring(lass()ypeParameters()));
/* Output
[E]
[K, V]
*/
我們可能期望能夠獲得真實的泛型參數,但是僅僅獲得了聲明時泛型參數佔位符。getTypeParameters 方法的 Javadoc 也是這麼解釋的:僅返回聲明時的泛型參數。所以,通過 getTypeParamters 方法無法獲得運行時的泛型信息。
運行泛型信息的獲取
但是在有些場景中,我們還是需要獲取泛型信息的。比如,在調用 HTTP 或 RPC 接口時,我們需要進行序列化和反序列的工作。
例如,我們通過一個 HTTP 接口接收如下的 JSON 數據
Java代碼
[{
"name": "Stark",
"nickName": "Iron Man"
},
{
"name": "Rogers",
"nickName": "Captain America"
}]
我們需要將其映射為 List。
但是之前我們提到了泛型擦除,那我們所使用的 HTTP 或 RPC 框架是如何獲取 List 中的泛型信息呢?
再看一段代碼
Java代碼
Map map = new HashMap() {};
Type type = lass()enericSuperclass();
ParameterizedType parameterizedType = (type);
for (Type typeArgument : ctualTypeArguments()) {
tln(ypeName());
}
/* Output
ng
ger
*/
上面這段代碼展示瞭如何獲取 map 這個實例所對應類的泛型信息。顯然,這次我們成功獲得了其中泛型參數信息。有了這些泛型參數,上面所提到的序列化和反序列化工作就是可能的`了。
那為什麼之前不可以,而這次可以了呢?請注意一個細節
上一節的變量聲明
Java代碼
Map map = new HashMap();
本節的變量聲明
Java代碼
Map map = new HashMap() {};
其中最關鍵的差別是本節的變量聲明多了一對大括號。有一定 Java 基礎的同學都能看出本節的變量聲明其實是創建了一個匿名內部類。這個類是 HashMap 的子類,泛型參數限定為了 String 和 Integer。
其實在“泛型擦除”一節,我們已經提到,Java 引入泛型擦除的原因是避免因為引入泛型而導致運行時創建不必要的類。那我們其實就可以通過定義類的方式,在類信息中保留泛型信息,從而在運行時獲得這些泛型信息。
簡而言之,Java 的泛型擦除是有範圍的,即類定義中的泛型是不會被擦除的。
框架中的應用
其實很多框架就是使用類定義中的泛型不會被擦除這個特性,實現了相應的功能。
例如,Spring Web 模塊的 RestTemplate,我們可以使用如下寫法:
Java代碼
ResponseEntity responseEntity = ange(url, , null, new ParameterizedTypeReference() {});
其中的 new ParameterizedTypeReference() {} 就是通過定義一個匿名內部類的方式來獲得泛型信息,從而進行反序列化的工作。
總結
Java 泛型擦除是 Java 泛型中的一個重要特性,其目的是避免過多的創建類而造成的運行時的過度消耗。所以,想 ArrayList 和 ArrayList 這兩個實例,其類實例是同一個。
但很多情況下我們又需要在運行時獲得泛型信息,那我們可以通過定義類的方式(通常為匿名內部類,因為我們創建這個類只是為了獲得泛型信息)在運行時獲得泛型參數,從而滿足例如序列化、反序列化等工作的需要。
只要理解了 Java 引入泛型擦除的原因,也自然能理解如何在運行時獲取泛型信息了。
-
java環境變量設置path
導語:java的環境變量是怎麼設置的呢?以下小編為大家介紹java環境變量設置path文章,僅供參考!java環境變量設置path環境變量是一個具有特定名字的對象,它包含了一個或者多個應用程序所將使用到的信息。例如path,當要求系統運行一個程序而沒有告訴它程序所在的完整路...
-
java中length和length()的區別
泉水,奮鬥之路越曲折,心靈越純潔。以下是小編為大家搜索整理了java中length和length()的區別,希望能給大家帶來幫助!更多精彩內容請及時關注我們應屆畢業生考試網!length是屬性,一般集合類對象擁有的屬性,取得集合的大小。例如:數組th就是取得數組的長度。length()是...
-
關於java如何配置MyEclipse Maven環境具體方法
雖然我的大部分項目已經遷到Idea上去了,但是在寫部分小的測試程序的時候還是習慣性的會點開MyEclipse。之前使用第三方庫的時候我會習慣的下載jar包,然後buildpath導入,但是在idea中貌似通過配置maven依賴更方便,於是我在MyEclipse中也想使用來導入依賴,在嘗試的過...
-
Java網絡基礎和Socket通信基礎
導語:Java是一門面向對象編程語言,不僅吸收了C++語言的各種優點,還摒棄了C++裏難以理解的多繼承、指針等概念,因此Java語言具有功能強大和簡單易用兩個特徵。下面我們來看看Java網絡基礎和Socket通信基礎,希望對大家有所幫助。一、網絡基礎1、InetAddressInetAddres...