Java 泛型程序設計

語言: CN / TW / HK

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

  • 通配符捕獲   可以通過泛型方法捕獲通配符