1. 程式人生 > >泛型中extends和super差別

泛型中extends和super差別

<? extends T>和<? super T>含有JAVA5.0的新的概念。由於它們的外表導致了很多人誤解了它們的用途:

1.<? extends T>首先你很容易誤解它為繼承於T的所有類的集合,這是大錯特錯的,相信能看下去你一定見過或用過List<? extends T>吧?為什麼我說理解成一個集合是錯呢?如果理解成一個集合那為什麼不用List<T>來表示?所以<? extends T>不是一個集合,而是T的某一種子類的意思,記住是一種,單一的一種,問題來了,由於連哪一種都不確定,帶來了不確定性,所以是不可能通過 add()來加入元素。你或許還覺得為什麼add(T)不行?因為

<? extends T>是T的某種子類,能放入子類的容器不一定放入超類,也就是沒可能放入T。
2.<? super T>這裡比較容易使用,沒<? extends T>這麼多限制,這裡的意思是,以T類為下限的某種類,簡單地說就是T類的超類。但為什麼add(T)可以呢?因為能放入某一類的容器一定可以放入其子類,多型的概念。

擦除

也許泛型最具挑戰性的方面是擦除(erasure),這是 Java 語言中泛型實現的底層技術。擦除意味著編譯器在生成類檔案時基本上會拋開引數化類的大量型別資訊。編譯器用它的強制型別轉換生成程式碼,就像程式設計師在泛型出現之前手工所做的一樣。區別在於,編譯器開始已經驗證了大量如果沒有泛型就不會驗證的型別安全約束。

通過擦除實現泛型的含意是很重要的,並且初看也是混亂的。儘管不能將List<Integer> 賦給List<Number>,因為它們是不同的型別,但是 List<Integer> 和 List<Number> 型別的變數是相同的類!要明白這一點,請評價下面的程式碼:

new List<Number>().getClass() == new List<Integer>().getClass()

編譯器只為 List 生成一個類。當生成了 List 的位元組碼時,將很少剩下其型別引數的的跟蹤。

當生成泛型類的位元組碼時,編譯器用型別引數的擦除替換型別引數。

對於無限制型別引數 (<V>),它的擦除是 Object。對於上限型別引數(<K extends Comparable<K>>),它的擦除是其上限(在本例中是 Comparable)的擦除。對於具有多個限制的型別引數,使用其最左限制的擦除。

如果檢查生成的位元組碼,您無法說出 List<Integer> 和 List<String> 的程式碼之間的區別。型別限制 T 在位元組碼中被 T 的上限所取代,該上限一般是 Object。

多重限制

一個型別引數可以具有多個限制。當您想要約束一個型別引數比如說同時為 Comparable 和 Serializable 時,這將很有用。多重限制的語法是用“與”符號分隔限制:

class C<T extends Comparable<? super T>&Serializable>

萬用字元型別可以具有單個限制 —— 上限或者下限。一個指定的型別引數可以具有一個或多個上限。具有多重限制的型別引數可以用於訪問它的每個限制的方法和域。

型別形參和型別實參

在引數化類的定義中,佔位符名稱(比如 Collection<V> 中的 V)叫做型別形參(type parameter),它們類似於方法定義中的形式引數。在引數化類的變數的宣告中,宣告中指定的型別值叫做型別實參(type argument),它們類似於方法呼叫中的實際引數。但是實際中二者一般都通稱為“型別引數”。所以給出定義:

interface Collection<V> { ... }

和宣告:

Collection<String> cs = new HashSet<String>();

那麼,名稱 V(它可用於整個 Collection 介面體內)叫做一個型別形參。在 cs 的宣告中,String 的兩次使用都是型別實參(一次用於 Collection<V>,另一次用於 HashSet<V>)。

關於何時可以使用型別形參,存在一些限制。大多數時候,可以在能夠使用實際型別定義的任何地方使用型別形參。但是有例外情況。不能使用它們建立物件或陣列,並且不能將它們用於靜態上下文中或者處理異常的上下文中。還不能將它們用作父型別(class Foo<T> extends T),不能用於 instanceof 表示式中不能用作類常量

類似地,關於可以使用哪些型別作為型別實參,也存在一些限制。型別實參必須是引用型別(不是基本型別)、萬用字元、型別引數,或者其他引數化型別的實 例化。所以您可以定義 List<String>(引用型別)、List<?>(萬用字元)或者 List<List<?>>(其他引數化型別的例項化)。在帶有型別形參 T 的引數化型別的定義中,您也可以宣告 List<T>(型別形參)。