欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 通配符詳解:?、? extends、? super 一篇搞懂

 更新時間:2025年10月19日 09:36:31   作者:梵得兒SHI  
本文深入解析Java泛型中的通配符(Wildcard)機制,重點講解無界通配符(?)、上界通配符(? extends T)和下界通配符(? super T)的用法與區(qū)別,感興趣的可以了解一下

在 Java 泛型中,通配符(Wildcard)是解決 “泛型類型不確定” 問題的關鍵。你可能見過List<?>List<? extends Number>這樣的寫法,它們就是通配符的典型應用。通配符看似簡單,卻藏著不少細節(jié):什么時候用??? extends? super有啥區(qū)別?為什么有的場景能存元素,有的只能讀?今天我們就從泛型的 “不變性” 講起,徹底搞懂無界通配符、上界通配符和下界通配符的用法、場景和底層邏輯,配上直觀圖解,讓你一次掌握!

一、為什么需要通配符?—— 從泛型的 “不變性” 說起

泛型有個重要特性:不變性(Invariance)。簡單說就是:如果BA的子類,List<B>不是List<A>的子類。這個特性會導致一些反直覺的問題,而通配符正是為了解決這些問題而生。

泛型不變性的 “坑”

假設我們有類繼承關系:Dog extends Animal,現(xiàn)在看下面的代碼:

List<Dog> dogs = new ArrayList<>();
// 編譯報錯:List<Dog>不能賦值給List<Animal>
List<Animal> animals = dogs; 

為什么會報錯?因為如果允許這種賦值,會導致類型安全問題:

// 假設上面的賦值合法
animals.add(new Cat());  // 往本應存Dog的列表里加了Cat
Dog dog = dogs.get(0);   // 運行時會拋ClassCastException(Cat不能轉(zhuǎn)Dog)

泛型的不變性正是為了避免這種安全問題 ——編譯時就阻斷錯誤,而不是等到運行時。但這也帶來了新問題:如何編寫一個能處理 “任意 Animal 子類集合” 的通用方法?比如一個打印所有動物的方法,既想接收List<Dog>,也想接收List<Cat>。這時,通配符就派上用場了。

通配符的核心作用

通配符(?)表示 “未知類型”,它的出現(xiàn)打破了泛型的嚴格不變性,允許在一定范圍內(nèi) “靈活匹配” 泛型類型,同時又不破壞類型安全。

泛型不變性與通配符作用圖解

二、無界通配符:?(表示 “任意類型”)

無界通配符?表示 “未知的任意類型”,可以理解為? extends Object的簡寫。它的核心場景是:只需要使用 Object 類的通用方法,不需要關注具體類型。

1. 無界通配符的用法

當方法參數(shù)需要接收 “任意泛型類型”,且方法內(nèi)部只讀取元素(不修改),或只調(diào)用Object類的方法(如toString()、equals())時,用?

示例:打印任意集合的元素

// 無界通配符:可以接收List<String>、List<Integer>、List<Dog>等任意List
public static void printList(List<?> list) {
    for (Object obj : list) {  // 只能用Object接收元素
        System.out.println(obj);  // 調(diào)用Object的toString()
    }
}

// 調(diào)用示例
List<String> strList = Arrays.asList("a", "b");
List<Integer> intList = Arrays.asList(1, 2);
printList(strList);  // 合法
printList(intList);  // 合法

2. 無界通配符的限制:不能添加元素(除了 null)

因為?表示 “未知類型”,編譯器無法確定集合能接收什么類型的元素,所以不能添加非 null 元素(null 是所有類型的默認值,例外)。

List<?> list = new ArrayList<String>();
list.add(null);  // 合法(null是任意類型的實例)
// list.add("hello");  // 編譯報錯:無法確定list是否能接收String

3. 無界通配符 vs 泛型方法

可能有人會問:用泛型方法public static <T> void printList(List<T> list)不也能實現(xiàn)同樣的功能嗎?

兩者的區(qū)別在于:

  • 泛型方法的T是 “確定的未知類型”(調(diào)用時編譯器會推斷具體類型),適合需要在方法內(nèi)部使用類型一致的場景(如返回T類型結(jié)果);
  • 無界通配符?是 “完全未知的類型”,適合只需要Object方法的場景,代碼更簡潔。

無界通配符圖解

三、上界通配符:? extends T(表示 “T 或 T 的子類”)

上界通配符? extends T表示 “未知類型,但其必須是 T 或 T 的子類”。核心場景是:需要讀取元素,且元素類型是 T 的子類型(即 “Producer”—— 生產(chǎn)者,只產(chǎn)出 T 類型的元素)。

1. 上界通配符的用法

當方法需要從集合中讀取元素,且希望元素是某個類型的子類型(如從 “數(shù)字集合” 中讀取數(shù)字,不管是 Integer 還是 Double),用? extends T。

示例:獲取集合中的最大值(數(shù)字集合)

// 上界通配符:接收Number或其子類(Integer、Double等)的集合
public static double getMax(List<? extends Number> numbers) {
    double max = Double.MIN_VALUE;
    for (Number num : numbers) {  // 安全讀取為Number類型
        if (num.doubleValue() > max) {
            max = num.doubleValue();
        }
    }
    return max;
}

// 調(diào)用示例
List<Integer> ints = Arrays.asList(1, 3, 2);
List<Double> doubles = Arrays.asList(1.5, 3.8, 2.2);
System.out.println(getMax(ints));   // 3.0
System.out.println(getMax(doubles));// 3.8

2. 上界通配符的限制:不能添加元素(除了 null)

和無界通配符類似,? extends T的具體類型未知(可能是 T 的任意子類),編譯器無法確定集合能接收什么類型的元素,因此不能添加非 null 元素。

List<? extends Number> nums = new ArrayList<Integer>();
nums.add(null);  // 合法
// nums.add(1);    // 編譯報錯:無法確定nums是否能接收Integer(可能是List<Double>)
// nums.add(3.14); // 編譯報錯:同理,可能是List<Integer>

3. 為什么上界通配符適合 “讀取”?

        因為不管具體類型是 T 的哪個子類,都可以安全地向上轉(zhuǎn)型為 T。例如List<? extends Number>中的元素,無論實際是 Integer 還是 Double,都能被Number類型接收,因此讀取操作是安全的。

上界通配符圖解

四、下界通配符:? super T(表示 “T 或 T 的父類”)

下界通配符? super T表示 “未知類型,但其必須是 T 或 T 的父類”。核心場景是:需要向集合中添加元素,且元素類型是 T 的子類型(即 “Consumer”—— 消費者,只接收 T 類型的元素)。

1. 下界通配符的用法

當方法需要向集合中添加元素,且希望元素是某個類型的子類型(如向 “動物集合” 中添加狗或貓,因為它們都是動物),用? super T。

示例:向集合中添加元素(動物集合)

// 下界通配符:接收Animal或其父類(如Object)的集合
public static void addDogs(List<? super Animal> animals) {
    animals.add(new Dog());  // 安全添加Dog(Animal的子類)
    animals.add(new Cat());  // 安全添加Cat(Animal的子類)
}

// 調(diào)用示例
List<Animal> animalList = new ArrayList<>();
List<Object> objectList = new ArrayList<>();
addDogs(animalList);  // 合法
addDogs(objectList);  // 合法

2. 下界通配符的限制:讀取元素只能作為 Object

因為? super T的具體類型是 T 的父類(可能是 T、T 的父類、Object),編譯器無法確定具體是哪個父類,所以讀取元素時只能用 Object 接收。

List<? super Animal> animals = new ArrayList<Object>();
animals.add(new Dog());  // 合法
Object obj = animals.get(0);  // 只能用Object接收
// Animal animal = animals.get(0);  // 編譯報錯:可能是List<Object>,不能確定是Animal

3. 為什么下界通配符適合 “寫入”?

因為 T 的子類可以安全地向上轉(zhuǎn)型為 T 的父類。例如List<? super Animal>可以接收 Animal 的子類(Dog、Cat),因為它們都能被轉(zhuǎn)型為 Animal(或其父類),因此寫入操作是安全的。

下界通配符圖解

五、通配符的經(jīng)典原則:PECS

面對三種通配符,很多人會混淆什么時候用哪個。記住這個經(jīng)典原則:PECS(Producer Extends, Consumer Super)—— 生產(chǎn)者用 extends,消費者用 super。

  • Producer(生產(chǎn)者):如果你需要從集合中讀取元素(集合產(chǎn)出元素給你),用? extends T。例:從 “數(shù)字集合” 中讀取數(shù)字計算總和,集合是生產(chǎn)者。

  • Consumer(消費者):如果你需要向集合中寫入元素(你給集合傳入元素),用? super T。例:向 “動物集合” 中添加狗,集合是消費者。

  • 既讀又寫:不要用通配符,直接用具體的泛型類型(如List<T>)。

PECS 原則圖解

六、常見誤區(qū)與避坑指南

  1. 通配符不能用于泛型類 / 接口的定義通配符只能用于方法參數(shù)、變量聲明,不能在定義泛型類或接口時使用:

    // 錯誤:泛型類定義不能用通配符
    class MyClass<?> { ... }
    
  2. List<?> 與 List<Object> 不同

    • List<Object> 可以添加任意類型的元素(因為 Object 是所有類的父類);
    • List<?> 不能添加非 null 元素(因為類型完全未知)。
  3. 上界和下界的嵌套使用復雜場景可能需要嵌套通配符,例如Map<? extends K, ? super V>,遵循 PECS 原則即可:鍵是生產(chǎn)者(用 extends),值是消費者(用 super)。

  4. 通配符與泛型方法的選擇

    • 若方法需要返回與輸入同類型的元素,用泛型方法(如public <T> T getFirst(List<T> list));
    • 若方法只需要讀取或?qū)懭?,且不關心具體類型,用通配符更簡潔。

七、總結(jié)

通配符是 Java 泛型中提升靈活性的關鍵,核心要點:

通配符類型語法含義適用場景操作限制
無界通配符?任意類型只讀取,用 Object 方法可添加 null,讀取用 Object
上界通配符? extends TT 或 T 的子類讀取元素(生產(chǎn)者)可添加 null,讀取用 T
下界通配符? super TT 或 T 的父類寫入元素(消費者)可添加 T 及其子類,讀取用 Object

記住 PECS 原則:“Producer Extends, Consumer Super”,遇到泛型集合操作時,先判斷是 “讀” 還是 “寫”,再選擇對應的通配符。

通配符的設計體現(xiàn)了 Java 泛型 “安全與靈活平衡” 的思想 —— 既打破了嚴格的不變性,又通過編譯時檢查保證類型安全。熟練掌握通配符,能讓你在處理集合、泛型組件時寫出更簡潔、更通用的代碼!

到此這篇關于Java 通配符詳解:?、? extends、? super 一篇搞懂的文章就介紹到這了,更多相關Java 通配符 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 將Java對象序列化成JSON和XML格式的實例

    將Java對象序列化成JSON和XML格式的實例

    下面小編就為大家分享一篇將Java對象序列化成JSON和XML格式的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • Java C++ leetcode執(zhí)行一次字符串交換能否使兩個字符串相等

    Java C++ leetcode執(zhí)行一次字符串交換能否使兩個字符串相等

    這篇文章主要為大家介紹了Java C++ leetcode1790執(zhí)行一次字符串交換能否使兩個字符串相等,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • SpringBoot熱重啟配置詳解

    SpringBoot熱重啟配置詳解

    在本篇文章里小編給大家分享的是關于SpringBoot熱重啟配置知識點內(nèi)容,需要的朋友們可以學習下。
    2020-02-02
  • spring中的@MapperScan注解屬性解析

    spring中的@MapperScan注解屬性解析

    @MapperScan是Spring集成MyBatis時自動掃描Mapper接口的注解,簡化配置并支持多數(shù)據(jù)源,通過屬性控制掃描路徑和過濾條件,利用動態(tài)代理生成Bean,需注意路徑匹配及注解優(yōu)先級,優(yōu)化項目配置與性能,本文給大家介紹spring中的@MapperScan注解,感興趣的朋友一起看看吧
    2025-07-07
  • mybatis多個plugins的執(zhí)行順序解析

    mybatis多個plugins的執(zhí)行順序解析

    這篇文章主要介紹了mybatis多個plugins的執(zhí)行順序解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • java8中forkjoin和optional框架使用

    java8中forkjoin和optional框架使用

    這篇文章主要介紹了java8中forkjoin和optional框架使用心得以及用法講解,需要的朋友參考下吧。
    2017-12-12
  • 基于spring boot 的配置參考大全(推薦)

    基于spring boot 的配置參考大全(推薦)

    下面小編就為大家?guī)硪黄趕pring boot 的配置參考大全(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Spring Cloud 2020.0.0正式發(fā)布再見了Netflix

    Spring Cloud 2020.0.0正式發(fā)布再見了Netflix

    這篇文章主要介紹了Spring Cloud 2020.0.0正式發(fā)布再見了Netflix,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • mybatis?傳入null值的解決方案

    mybatis?傳入null值的解決方案

    這篇文章主要介紹了mybatis?傳入null值的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • JDK動態(tài)代理接口和接口實現(xiàn)類深入詳解

    JDK動態(tài)代理接口和接口實現(xiàn)類深入詳解

    這篇文章主要介紹了JDK動態(tài)代理接口和接口實現(xiàn)類,JDK動態(tài)代理是代理模式的一種實現(xiàn)方式,因為它是基于接口來做代理的,所以也常被稱為接口代理,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06

最新評論