Java泛型與型別擦除
“編譯器會進行泛型擦除”是一個常識了(好吧,實際擦除的是引數和自變數的型別)。這個過程由“型別擦除”實現。但是並非像許多開發者認為的那樣,在 <..> 符號內的東西都被擦除了。看下面這段程式碼:
public class ClassTest { public static void main(String[] args) throws Exception { ParameterizedType type = (ParameterizedType) Bar.class.getGenericSuperclass(); System.out.println(type.getActualTypeArguments()[0]); ParameterizedType fieldType = (ParameterizedType) Foo.class.getField("children").getGenericType(); System.out.println(fieldType.getActualTypeArguments()[0]); ParameterizedType paramType = (ParameterizedType) Foo.class.getMethod("foo", List.class) .getGenericParameterTypes()[0]; System.out.println(paramType.getActualTypeArguments()[0]); System.out.println(Foo.class.getTypeParameters()[0] .getBounds()[0]); } class Foo<E extends CharSequence> { public List<Bar> children = new ArrayList<Bar>(); public List<StringBuilder> foo(List<String> foo) {return null; } public void bar(List<? extends String> param) {} } class Bar extends Foo<String> {} }
你知道輸出了什麼嗎?
class java.lang.String class ClassTest$Bar class java.lang.String class java.lang.StringBuilder interface java.lang.CharSequence
你會發現每一個型別引數都被保留了,而且在執行期可以通過反射機制獲取到。那麼到底什麼是“型別擦除”?至少某些東西被擦除了吧?是的。事實上,除了結構化資訊外的所有東西都被擦除了 —— 這裡結構化資訊是指與類結構相關的資訊,而不是與程式執行流程有關的。換言之,與類及其欄位和方法的型別引數相關的後設資料都會被保留下來,可以通過反射獲取到。
而其他的資訊都被擦除掉了。例如下面這段程式碼:
List<String> list = new ArrayList<>(); Iterator<String> it = list.iterator(); while (it.hasNext()) { String s = it.next(); }
實際上會被轉換成這個(這兩段程式碼的位元組碼是一致的)
List list = new ArrayList(); Iterator it = list.iterator(); while (it.hasNext()) { String s = (String) it.next(); }
因此,定義在方法體內的型別引數會被擦除,在必要的時候會有型別轉換。另外,如果一個方法被定義為接受 List 引數,這個 T 會被轉換成 Object (如果定義了型別的上界的話就轉換成對應的型別。這也是你不能 new T() 的原因)。(順便這裡有個關於型別擦除的問題)
目前為止型別擦除定義中的前兩點我們都講完了。第三點是關於bridge方法,我已經在 stackoverflow 上的這個問題(和回答)中已經說明了。
兩個結論。第一,java 泛型是非常複雜的。但是不用完全理解這些細節也可以使用它們。
第二,不要假設所有的型別資訊都被擦除了 —— 結構化的型別引數還存在,需要的話還是可以用下的(不過不要過分依賴反射機制)。
相關文章
- Java泛型型別擦除問題Java泛型型別
- Java 泛型,你瞭解型別擦除嗎?Java泛型型別
- Java泛型(三):型別擦除帶來的約束與侷限性Java泛型型別
- 初探Java型別擦除Java型別
- Java™ 教程(型別擦除)Java型別
- 泛型擦除的原理泛型
- 一句話,講清楚java泛型的本質(非型別擦除)Java泛型型別
- 面試官:說說什麼是泛型的型別擦除?面試泛型型別
- Swift 型別擦除Swift型別
- Java™ 教程(泛型原始型別)Java泛型型別
- Java泛型T與?的區別Java泛型
- 型別與泛型標記型別泛型
- 泛型的型別擦除後,fastjson反序列化時如何還原?泛型型別ASTJSON
- 【java】【泛型】泛型geneticJava泛型
- java泛型之泛型方法。Java泛型
- 型別 VS 泛型型別泛型
- TypeScript 泛型型別TypeScript泛型型別
- Java泛型知識點:泛型類、泛型介面和泛型方法Java泛型
- java中泛型之型別萬用字元(?)Java泛型型別字元
- Java中建立泛型型別的例項Java泛型型別
- Java泛型理解與使用Java泛型
- java泛型之泛型陣列。Java泛型陣列
- [譯]Swift 中的型別擦除Swift型別
- 泛型型別(.NET 指南)泛型型別
- Java 泛型Java泛型
- Java泛型Java泛型
- JAVA基礎之九-泛型(通用型別)Java泛型型別
- 泛型類、泛型方法、型別萬用字元的使用泛型型別字元
- Java中的泛型程式設計:深入理解型別引數與型別邊界的使用Java泛型程式設計型別
- Scala 泛型型別和方法泛型型別
- 如何使用Java泛型對映不同的值型別Java泛型型別
- Java 泛型原理Java泛型
- Java+泛型Java泛型
- java泛型一二Java泛型
- Java(7)泛型Java泛型
- Java-泛型Java泛型
- java泛型剖析Java泛型
- JAVA泛型類Java泛型