基礎程式設計(Java)· CH20

泛型類別與方法

為何用泛型 · 泛型方法 · 泛型類別 · 有界型別 · 多型別參數 · 萬用字元 · 導覽地圖

完整內容看 PDF 講義,型別替換互動與試跑看 index。

全章主線

泛型解決什麼問題

沒有泛型(用 Object)
取出要手動轉型
放錯型別執行期才爆
ClassCastException
有泛型 <T>
編譯期就擋型別錯
取出免轉型
一份程式碼套多型別

兩大好處:編譯期型別安全免手動轉型

泛型方法

<T> 宣告在回傳型別前

public static <T> void printArray(T[] a) {
    for (T e : a) System.out.print(e + " ");   // 方法內用 T
}
printArray(ints);    // T 自動推斷為 Integer
printArray(words);   // T 自動推斷為 String

<T> 放在 static 之後、回傳型別之前。一份方法取代多個只有型別不同的多載。

泛型類別

class Box<T>:T 是型別佔位符

public class Box<T> {
    private T value;                 // 欄位型別是 T
    public T get() { return value; }
}
Box<Integer> bi = new Box<>(42);   // 菱形 <> 推斷
int n = bi.get();                    // 不必轉型

填不同型別實參,編譯器把 T 替換成具體型別。static 方法用不到類別的 T

型別實參

不能用基本型別

Box<Integer> ok;     // 參考型別(包裝類別)
// Box<int> bad;     // ← 編譯錯:基本型別不能當型別實參

型別參數=定義時的佔位符 T型別實參=使用時填的 Integer。型別實參必須是參考型別(自動裝箱幫忙在 intInteger 間轉換)。

有界型別參數

<T extends Comparable<T>>

public static <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;   // 有界才能呼叫 compareTo
}

沒上界時 T 只當 Object,呼叫不到 compareTo上界extends 後那個型別;泛型一律寫 extends(涵蓋類別與介面)。

多型別參數

<K, V>:一個泛型多個型別

public class Pair<K, V> {
    private K key;
    private V value;
}
Pair<String, Integer> p = new Pair<>("age", 18);

逗號分隔、各自獨立。Map<K, V>(CH16)就是標準例。慣例:T 型別、E 元素、K 鍵、V 值。

萬用字元

? · ? extends · ? super(PECS)

List<?>               // 無界:任何型別,只能讀為 Object、不能 add
List<? extends Number> // 上界:讀(producer),不能 add
List<? super Integer>  // 下界:寫(consumer),可 add Integer

泛型不協變List<Integer> 不是 List<Number> 的子型別。口訣 PECS:要讀用 extends、要寫用 super

底層機制

型別抹除(了解即可)

Box<Integer> bi; Box<String> bs;
bi.getClass() == bs.getClass();   // true(都是 Box.class)
  • 泛型是編譯期機制;編譯後 T 被抹成上界(預設 Object
  • 執行期不留泛型資訊 → 不能 new T[]T.classinstanceof List<String>
  • 好處:與 Java 5 之前沒有泛型的舊程式碼相容

接下來

把泛型方法與泛型類別練到自動

  • PDF 講義 完整內容+型別替換/取代多載藍圖圖
  • index 型別參數替換互動+試跑+多載收斂步進
  • 本地練習 5 題 泛型方法/Pair/有界 max/Box/swap
  • 複習測驗 自測到 90%