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

深入理解 Java 泛型通配符:? super T 與 ? extends T 的核心差異與 PECS 原則

     編輯:Java 綜合教學

深入理解 Java 泛型通配符:? super T 與 ? extends T 的核心差異與 PECS 原則

在 Java 泛型編程中,List<? super T> 與 List<? extends T> 是兩種極常見卻又容易混淆的通配符聲明。它們分別代表「下界通配符」與「上界通配符」,決定了集合的讀寫能力邊界。本文通過具體代碼場景,徹底解析兩者的區別、適用場景及核心原則 PECS。

? extends T —— 上界通配符(生產者)

聲明 List<? extends Number> foo3 時,合法賦值包括:

List<? extends Number> foo3 = new ArrayList<Number>();
List<? extends Number> foo3 = new ArrayList<Integer>();
List<? extends Number> foo3 = new ArrayList<Double>();

讀取操作: 你一定能讀到 Number 或其子類型對象,但無法保證具體是 Integer 或 Double,因為實際列表類型未知。

寫入操作: 完全禁止 添加任何具體類型對象(含 Number、Integer、Double),因為無法保證類型安全。唯一例外是添加 null。

結論:? extends T 適合「只讀、不寫」的生產者場景,你從中取出的永遠是 T 或其子類。

? super T —— 下界通配符(消費者)

聲明 List<? super Integer> foo3 時,合法賦值包括:

List<? super Integer> foo3 = new ArrayList<Integer>();
List<? super Integer> foo3 = new ArrayList<Number>();
List<? super Integer> foo3 = new ArrayList<Object>();

讀取操作: 唯一能保證讀到的是 Object 類型,無法確保是 Integer 或 Number。寫入操作: 可以安全添加 Integer 及其子類對象,因為所有合法列表都支援 Integer。但不能添加 Number、Object 或無關類型(如 Double)。

結論:? super T 適合「只寫、不讀」的消費者場景,你放入的永遠是 T 或其子類。

PECS 原則:生產者用 extends,消費者用 super

  • 生產者:若需從列表讀取 T 類型元素 → 宣告 <? extends T>(如 List<? extends Integer>),不可添加元素。
  • 消費者:若需向列表寫入 T 類型元素 → 宣告 <? super T>(如 List<? super Integer>),讀取類型不確定。
  • 既是生產者又是消費者:不可使用通配符,需用確切泛型(如 List<Integer>)。

實戰範例:JDK Collections.copy()

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    for (int i = 0; i < src.size(); i++) {
        dest.set(i, src.get(i));
    }
}

src 為生產者(讀取),用 ? extends T;dest 為消費者(寫入),用 ? super T。這完美體現了 PECS 原則對參數類型的保護。

總結速查表

通配符別名可讀類型可寫類型適用場景
? extends T上界/生產者T 及其子類無(除 null)讀取數據、遍歷、轉換
? super T下界/消費者ObjectT 及其子類添加數據、累積、過濾
T (無通配符)確切類型T 及其子類T 及其子類需同時讀寫

掌握 PECS,即掌握了 Java 泛型通配符的靈魂,能寫出更安全、靈活的 API。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved