Java 泛型不具協變性:List<Integer> 不是 List<Number> 的子型別。若方法簽名為 void put(Box<Number> box),無法傳入 Box<Integer>,嚴重限制 API 靈活性。有界通配符正是為此而生:
? extends T —— 上界:讀取場景,表示「T 或 T 的子型別」。? super T —— 下界:寫入場景,表示「T 或 T 的父型別」。| 場景 | 通配符 | 典型方法 | 說明 |
|---|---|---|---|
| 只讀取(生產者) | ? extends T | T get() | 取出元素安全視為 T;不可寫入(除 null) |
| 只寫入(消費者) | ? super T | void put(T) | 放入 T 安全;取出只能視為 Object |
| 既讀又寫 | 無通配符(確切類型) | T get(); void put(T) | 需精確型別,失去多態彈性 |
interface Box<T> {
T get();
void put(T item);
// 讀取協變:允許傳入 Box<Integer> 給 Box<? extends Number>
void putFrom(Box<? extends T> src);
// 寫入逆變:允許將 Box<Number> 當作 Box<? super Integer>
void copyTo(Box<? super T> dest);
}
Collection<T> 而非 Collection<? extends T>void process(Collection<Integer> c) → void process(Collection<? extends Number> c),否則無法傳入 List<Integer> 給期望 Number 的 API。? super Tvoid sort(List<T> list, Comparator<? super T> cmp),允許 Comparator<Object> 比較 String。AbstractExecutorService.invokeAll(Collection<Callable<T>>) 初版強制 Callable<T>,導致無法傳入 List<MyTask extends Callable<String>>。Java 6 修正為 Collection<? extends Callable<T>>,完美詮釋 PECS。
「只讀用 extends,只寫用 super,既讀又寫不用通配符」—— 牢記 PECS,API 設計不再踩坑。