首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 泛型通配符深度理解:置換原則、extends/super 差異與 PECS 原則實戰

     編輯:Java 相關知識

置換原則與泛型的「反直覺」

物件導向: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

限制 1:不能直接建立變數

List<? extends Number> nums = new ArrayList<Integer>(); // 編譯錯誤

限制 2:不可寫入(新增/修改)

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。

PECS 原則:Producer Extends, Consumer Super

場景通配符操作範例
只讀取(生產元素)? extends Tget(), 遍歷void copy(Collection<? extends T> src, List<T> dest)
只寫入(消費元素)? super Tadd(), set()void fill(List<? super T> dest, T val)
既讀又寫無通配符完整操作List<T>

總結

  1. 通配符修飾的泛型**不能直接實例化變數**
  2. ? extends T:安全**讀取**,禁止寫入 — 適合生產者
  3. ? super T:安全**寫入**,讀取只能當 Object — 適合消費者
  4. 方法簽名善用通配符提升 API 靈活性(如 Collections.copy、Collections.sort)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved