<? extends T>、<? super T>、<T extends T> 異同與最佳實踐泛型通配符是 Java 類型系統中最令人困惑也最強大的部分。本文通過 Fruit → Apple/Orange 繼承體系,系統闡述六大核心問題。
class Fruit {}
class Apple extends Fruit {}
class Orange extends Fruit {}
List<Fruit> 不是 List<Apple> 的父類型?兩大原因:
List,無繼承關係。List<Fruit> list = new ArrayList<Apple>(),則可 list.add(new Orange()),破壞 List<Apple> 的型別承諾。<? extends Fruit> 存在的必要性 —— 生產者需求:方法同時接受 List<Fruit>、List<Apple>、List<Orange>。
解法:void process(List<? extends Fruit> list)
add()(除 null)。<? super Fruit> 與 <? extends Fruit> 差異 —— 消費者void addFruits(List<? super Fruit> list)
add(new Apple())、add(new Fruit());獲取時類型擦除為 Object。PECS 原則:Producer Extends, Consumer Super。只讀用 extends,只寫用 super,既讀又寫不用通配符。
<? extends Fruit> vs <T extends Fruit>| 語法 | 位置 | 用途 |
|---|---|---|
<T extends Fruit> | 類/方法宣告 | 定義型別參數,方法內可用 T 宣告變數、返回值、參數 |
<? extends Fruit> | 參數化型別實參 | 實例化泛型時指定上界,不可在程式碼中引用 ? |
// 方法型別參數
public <E extends Fruit> void copy(List<E> src, List<E> dest) { ... }
// 通配符實參
public void print(List<? extends Fruit> list) { ... }
List vs List<?>List(原生型別):關閉泛型檢查,可任意 add,不安全,僅為相容舊代碼。List<?>(無界通配符):表示「未知型別的 List」,禁止 add(除 null),讀取為 Object,類型安全。建議:新代碼一律用 List<?> 代替原生 List。
class A<T extends A<T>>經典模式:Enum<E extends Enum<E>>、Comparable<T extends Comparable<T>>。
class Base<T extends Base<T>> {
void set(T arg) { System.out.println("in Base"); }
}
class SubA extends Base<SubA> {}
class SubB extends Base<SubB> {}
// 用法
SubA a1 = new SubA(), a2 = new SubA();
a1.set(a2); // OK
// a1.set(new SubB()); // 編譯錯誤
// a1.set(new Base<>()); // 編譯錯誤
意義:基類方法參數/返回型別精確為具體子類型,實現「類型安全的鏈式呼叫」與「複製模式」。
| 語法 | 讀/寫 | 典型場景 | 關鍵字 |
|---|---|---|---|
<? extends T> | 讀/不可寫 | 生產者、複製來源 | PECS - Extends |
<? super T> | 寫/讀為 Object | 消費者、複製目標 | PECS - Super |
<T extends Bound> | 完全可控 | 泛型方法/類定義 | 型別參數 |
| 讀為 Object/不可寫 | 未知型別集合、工具方法 | 無界通配符 |