? 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 或其子類。
<? extends T>(如 List<? extends Integer>),不可添加元素。<? super T>(如 List<? super Integer>),讀取類型不確定。List<Integer>)。
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 | 下界/消費者 | Object | T 及其子類 | 添加數據、累積、過濾 |
T (無通配符) | 確切類型 | T 及其子類 | T 及其子類 | 需同時讀寫 |
掌握 PECS,即掌握了 Java 泛型通配符的靈魂,能寫出更安全、靈活的 API。