Java 泛型程式設計
1. 泛型類
public class Pair<T>
{
private T first;
private T second;
public void setSecond(T second){...}
....
}
2. 泛型方法
class ArrAlg{
public static <T> T getMiddle(T... a){
return a[a.length/2];
}
}
3. 類型變數的限定
public static <T extends BoundingType1 & BoundingType2 ...> T min(T[] a)....
這表示T應該是BoundingType類型的子類型。T和綁定類型可以是類,也可以是介面 。一個類型變數或通配符可以有多個限定,但限定中之多只有一個類,這是因為單繼承,當有多個限定時,基類要寫在第一個,介面寫在後面。
4. 原始類型,任何一個泛型類型,都自動提供一個原始類型,原始類型的名字就是刪去類型參數後的類型名。例如Pair<T>, 原始類型就是Pair。
5. 泛型轉換
- 虛擬機中沒有泛型,只有普通的類和方法
- 所有的類型參數都用它們的第一個限定類型替換,沒有寫明限定類型就用Object
- 合成橋方法保持多態。
- 為保持類型安全性,必要時插入強制類型轉換(例如調用泛型方法返回值)
5. 橋方法
假設有下列類繼承了Pair<Date>,
class DateInterval extends Pair<Date>
{
public void setSecond(Date second){
....
}
}
實際擦除類型之後
class DateInterval extends Pair
{
public void setSecond(Date second){...}
//因為類型擦除,實際還存在一個繼承於Pair類的方法,顯然這是兩個方法
public void setSecond(Object second){
//實際生成橋方法,這裡會調用setSecond(Date seconde);即:
setSecond((Date)second);
}
}
當如下調用時 Pair<Date> pair = new DateInterval(...); pair.setSecond(aDate);。Pair類型只有一個方法setSecond(Object)。實際引用DateInterval類,因而將會調用DateInterval.setSecond(Object)。編譯器為了調用最合適的方法,實際上生成了一個橋方法,如下 public void setSecond(Object second){ setSecond((Date) second) } 。
6. 約束與局限性
- 不能用基本類型實例化類型參數
- 運行時類型查詢只適用於原始類型
- 不能創建參數化類型的數組,即不能
new Pair<String> [10] - 不能實例化類型變數,即不能使用 new T(…), new T[] 或T.class這類的表達式。可以通過反射實例化T但不能T.class.newInstance(); 可以如下設計API來實現。
public static <T> Pair<T> makePair(Class<T> cl){
try{
return new Pair<>(cl.newInstance(), cl.newInstance())
}catch(Exception e){return null;}
}
- 泛型類的靜態上下文中不能引用類型變數,例如
private static T aData; 或者 public static T fun(){}都是錯誤的。 - 不能拋出或捕獲泛型類的實例
7. 通配符
- 通配符限定: extends 、super
例:Pair<? extends Person>表示泛型Pair類型,它的參數是Person的子類。
在這種情況下,getter和setter區別,getter可以正常調用,但是setter不行,會產生編譯錯誤,因為編譯器不能確定要傳入參數的類型,也沒法代替。
?extends Person getFirst()
void setFirst(? extends Person)
反之,通配符的超類型限定: ?super Student
void setFirst(? super Student);
? super Student getFirst();
編譯器雖然不知道setFirst的確切類型,但是可以用任意Student對象調用,而不能用Person對象調用。如果調用getFirst,返回的對象不能保證,只能賦給Object。
- 無限定通配符 Pair<?>
? getFirst();
void setFirst(?);
getFirst的返回值只能賦給一個Object, setFirst不能調用除非setFirst(null),對於一些簡單操作非常有用,例如判定是否為null getFirst() == null
- 通配符捕獲 可以通過泛型方法捕獲通配符

