物件導向:Child 可賦值給 Parent 變數。
泛型:**List<Child> 不能賦值給 List<Parent>** — 編譯錯誤。
List<Integer> ints = new ArrayList<>();
List<Number> nums = ints; // Error: 不相容型別
原因:若允許,可向 nums 加入 Double,導致 ints 內含非 Integer 物件,破壞型別安全。
? extends T — 生產者// Collection.addAll 簽名
boolean addAll(Collection<? extends E> c);
允許傳入 List<Integer>、List<Double> 給 List<Number>.addAll()。
List<Number> nums = new ArrayList<>();
nums.addAll(Arrays.asList(1, 2)); // Integer
nums.addAll(Arrays.asList(2.78, 3.14)); // Double
List<? extends Number> nums = new ArrayList<Integer>(); // 編譯錯誤
List<Integer> ints = new ArrayList<>();
List<? extends Number> nums = ints; // OK:賦值給上界通配符引用
nums.add(3.14); // 編譯錯誤!編譯器不知實際型別,禁止寫入
核心邏輯:nums 實際指向 List<Integer>,若允許 add(Double) 會污染原列表。
但可讀取:Number n = nums.get(0); 安全,因取出的必是 Number 子類。
? super T — 消費者List<Object> objs = new ArrayList<>();
List<? super Integer> consumer = objs; // OK
consumer.add(1); // OK:Integer 是 Object 子類
consumer.add(new Object()); // 錯誤:Object 非 Integer 父類
Integer i = consumer.get(0); // 錯誤:取出只能當 Object
寫入安全(參數為 Integer 或其子類),讀取只能當 Object。
| 場景 | 通配符 | 操作 | 範例 |
|---|---|---|---|
| 只讀取(生產元素) | ? extends T | get(), 遍歷 | void copy(Collection<? extends T> src, List<T> dest) |
| 只寫入(消費元素) | ? super T | add(), set() | void fill(List<? super T> dest, T val) |
| 既讀又寫 | 無通配符 | 完整操作 | List<T> |
? extends T:安全**讀取**,禁止寫入 — 適合生產者? super T:安全**寫入**,讀取只能當 Object — 適合消費者Collections.copy、Collections.sort)