Java基礎語法<十二> 泛型程序設計
1 意義
泛型程序設計意味著編寫的代碼可以被很多不同類型的對象所重用。
常見應用 : ArrayList
2 K T V E ? object等的含義
類型變量使用大寫形式
E – Element (在集合中使用,因為集合中存放的是元素)
T – Type(Java 類)(需要時還可以用臨近的字母U和S)表示任意類型 S、U、V – 2nd、3rd、4th types
K – Key(鍵)
V – Value(值)
N – Number(數值類型)
? – 表示不確定的java類型(無限制通配符類型)
Object – 是所有類的根類,任何類的對象都可以設置給該Object引用變量,使用的時候可能需要類型強制轉換,但是用使用了泛型T、E等這些標識符後,在實際用之前類型就已經確定了,不需要再進行類型強制轉換。
3 泛型方法
public static <T> T getXXX(){
}
類型變量放在修飾符的後面,返回類型的前面
變量類型的限定
<T extends Comparable> T
一個類型變量或通配符可以有多個限定
T extends Comparable & Serializable
限定類型用&分隔,逗號用來分隔類型變量
在Java的繼承中,可以根據需要擁有的多個接口超類型,但限定中至多有一個類,如果用一個類作為限定,它必須是限定列表中的第一個。
4 泛型代碼和虛擬機
Java中的泛型基本上都是在編譯器這個層次來實現的。
在生成的Java字節代碼中是不包含泛型中的類型信息的。
使用泛型的時候加上的類型參數,會被編譯器在編譯的時候去掉。 其實編譯器通過Code sharing方式為每個泛型類型創建唯一的字節碼表示,並且將該泛型類型的實例都映射到這個唯一的字節碼表示上。將多種泛型類形實例映射到唯一的字節碼表示是通過類型擦除(type erasue)實現的。
無論何時定義一個泛型類型,都自動提供一個相應的原始類型(raw type)。原始類型的名字就是刪除類型參數後的泛型類型名。擦除(erased)類型
4.1 類型擦除
類型擦除指的是通過類型參數合並,將泛型類型實例關聯到同一份字節碼上。編譯器只為泛型類型生成一份字節碼,並將其實例關聯到這份字節碼上。類型擦除的關鍵在於從泛型類型中清除類型參數的相關信息,並且再必要的時候添加類型檢查和類型轉換的方法。 類型擦除可以簡單的理解為將泛型java代碼轉換為普通java代碼,只不過編譯器更直接點,將泛型java代碼直接轉換成普通java字節碼。
類型擦除的主要過程如下:
1.將所有的泛型參數用其最左邊界(最頂級的父類型)類型替換。
2.移除所有的類型參數。
PS:
1.虛擬機中沒有泛型,只有普通類和普通方法,所有泛型類的類型參數在編譯時都會被擦除,泛型類並沒有自己獨有的Class類對象。比如並不存在List<String>.class或是List<Integer>.class,而只有List.class。
2.創建泛型對象時請指明類型,讓編譯器盡早的做參數檢查(Effective Java,第23條:請不要在新代碼中使用原生態類型)
3.不要忽略編譯器的警告信息,那意味著潛在的ClassCastException等著你。
4.靜態變量是被泛型類的所有實例所共享的。對於聲明為MyClass<T>的類,訪問其中的靜態變量的方法仍然是 MyClass.myStaticVar。不管是通過new MyClass<String>還是new MyClass<Integer>創建的對象,都是共享一個靜態變量。
5.泛型的類型參數不能用在Java異常處理的catch語句中。因為異常處理是由JVM在運行時刻來進行的。由於類型信息被擦除,JVM是無法區分兩個異常類型MyException<String>和MyException<Integer>的。對於JVM來說,它們都是 MyException類型的。也就無法執行與異常對應的catch語句。
5 泛型轉換
Java虛擬機中沒有泛型,只有普通的類和方法
所有的類型參數都用它們的限定類型替換
橋方法被合成來保持多態
為保持類型安全性,必要時插入強制類型轉換
6 約束與局限性
- 不能用基本類型實例化類型參數
- 運行時類型查詢只適用於原始類型
- 不能創建參數化類型的數組
- Varargs警告
- 不能實例化類型變量
- 泛型類的靜態上下文中類型變量無效
- 不能拋出或捕獲泛型類的實例
- 註意擦除後的沖突
7 通配符類型
7.1 <? extends T>
表示類型的上界,表示參數化類型的可能是T 或是 T的子類
7.2 <? super T >
表示類型下界(Java Core中叫超類型限定),表示參數化類型是此類型的超類型(父類型),直至Object
帶有超類型限定的通配符可以向泛型對象寫入,帶有子類型限定的通配符可以從泛型對象讀取。
7.3 無限定通配符 <?>
ps:
如果要從集合中讀取類型T的數據,並且不能寫入,可以使用 ? extends 通配符;(Producer Extends)
如果要從集合中寫入類型T的數據,並且不需要讀取,可以使用 ? super 通配符;(Consumer Super)
如果既要存又要取,那麽就不要使用任何通配符。
Java基礎語法<十二> 泛型程序設計