最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

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

泛型不變性的 “坑”

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

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

為什么會報(bào)錯?因?yàn)槿绻试S這種賦值,會導(dǎo)致類型安全問題:

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

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

通配符的核心作用

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

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

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

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

1. 無界通配符的用法

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

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

// 無界通配符:可以接收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)

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

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

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

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

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

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

無界通配符圖解

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

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

1. 上界通配符的用法

當(dāng)方法需要從集合中讀取元素,且希望元素是某個(gè)類型的子類型(如從 “數(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);    // 編譯報(bào)錯:無法確定nums是否能接收Integer(可能是List<Double>)
// nums.add(3.14); // 編譯報(bào)錯:同理,可能是List<Integer>

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

        因?yàn)椴还芫唧w類型是 T 的哪個(gè)子類,都可以安全地向上轉(zhuǎn)型為 T。例如List<? extends Number>中的元素,無論實(shí)際是 Integer 還是 Double,都能被Number類型接收,因此讀取操作是安全的。

上界通配符圖解

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

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

1. 下界通配符的用法

當(dāng)方法需要向集合中添加元素,且希望元素是某個(gè)類型的子類型(如向 “動物集合” 中添加狗或貓,因?yàn)樗鼈兌际莿游铮?,?code>? 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

因?yàn)?code>? super T的具體類型是 T 的父類(可能是 T、T 的父類、Object),編譯器無法確定具體是哪個(gè)父類,所以讀取元素時(shí)只能用 Object 接收

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

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

因?yàn)?T 的子類可以安全地向上轉(zhuǎn)型為 T 的父類。例如List<? super Animal>可以接收 Animal 的子類(Dog、Cat),因?yàn)樗鼈兌寄鼙晦D(zhuǎn)型為 Animal(或其父類),因此寫入操作是安全的。

下界通配符圖解

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

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

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

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

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

PECS 原則圖解

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

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

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

    • List<Object> 可以添加任意類型的元素(因?yàn)?Object 是所有類的父類);
    • List<?> 不能添加非 null 元素(因?yàn)轭愋屯耆粗?/li>
  3. 上界和下界的嵌套使用復(fù)雜場景可能需要嵌套通配符,例如Map<? extends K, ? super V>,遵循 PECS 原則即可:鍵是生產(chǎn)者(用 extends),值是消費(fèi)者(用 super)。

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

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

七、總結(jié)

通配符是 Java 泛型中提升靈活性的關(guān)鍵,核心要點(diǎn):

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

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

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

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

相關(guān)文章

  • 詳解Java設(shè)計(jì)模式之抽象工廠模式

    詳解Java設(shè)計(jì)模式之抽象工廠模式

    設(shè)計(jì)模式是軟件設(shè)計(jì)中的一種常見方法,通過定義一系列通用的解決方案,來解決常見的軟件設(shè)計(jì)問題,其中,抽象工廠模式是一種非常常見的設(shè)計(jì)模式,文中有詳細(xì)的代碼示例供大家參考,感興趣的同學(xué)可以借鑒閱讀
    2023-05-05
  • 詳解Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例

    詳解Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例

    本篇文章主要介紹了Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例,Spring框架主要提供了Set注入和構(gòu)造注入兩種依賴注入方式。有興趣的可以了解一下。
    2017-02-02
  • spring boot基于Java的容器配置講解

    spring boot基于Java的容器配置講解

    這篇文章主要介紹了spring boot基于Java的容器配置講解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • SpringBoot+MinIO實(shí)現(xiàn)對象存儲的示例詳解

    SpringBoot+MinIO實(shí)現(xiàn)對象存儲的示例詳解

    MinIO?是一個(gè)基于Apache?License?v2.0開源協(xié)議的對象存儲服務(wù),它是一個(gè)非常輕量的服務(wù),可以很簡單的和其他應(yīng)用的結(jié)合,所以下面我們就來看看SpringBoot如何整合MinIO實(shí)現(xiàn)對象存儲吧
    2023-10-10
  • 在 Spring Boot 中使用異步線程時(shí)的 HttpServletRequest 復(fù)用問題記錄

    在 Spring Boot 中使用異步線程時(shí)的 HttpServletReque

    文章討論了在SpringBoot中使用異步線程時(shí),由于HttpServletRequest復(fù)用導(dǎo)致的Cookie解析失敗問題,為了解決這個(gè)問題,文章推薦了使用HttpServletRequestWrapper創(chuàng)建請求副本、手動傳遞請求上下文和延遲請求清理等方法,感興趣的朋友一起看看吧
    2025-03-03
  • JDK21中Sequenced Collections(序列集合)的實(shí)現(xiàn)

    JDK21中Sequenced Collections(序列集合)的實(shí)現(xiàn)

    Sequenced Collections是Java中的一個(gè)新特性,它提供了一種有序的集合實(shí)現(xiàn),本文主要介紹了JDK21中Sequenced Collections(序列集合)的實(shí)現(xiàn),感興趣的可以了解一下
    2025-09-09
  • Java中一個(gè)線程執(zhí)行死循環(huán)有什么后果

    Java中一個(gè)線程執(zhí)行死循環(huán)有什么后果

    這篇文章主要為大家詳細(xì)介紹了Java中一個(gè)線程執(zhí)行死循環(huán)有什么后果,當(dāng)一個(gè)線程在執(zhí)行死循環(huán)時(shí)會影響另外一個(gè)線程嗎,下面為大家揭曉
    2016-05-05
  • java密鑰交換算法DH定義與應(yīng)用實(shí)例分析

    java密鑰交換算法DH定義與應(yīng)用實(shí)例分析

    這篇文章主要介紹了java密鑰交換算法DH定義與應(yīng)用,結(jié)合實(shí)例形式分析了Java密鑰交換算法DH的原理、定義、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • Java源碼刨析之ArrayDeque

    Java源碼刨析之ArrayDeque

    ArrayDeque是Deque接口的一個(gè)實(shí)現(xiàn),使用了可變數(shù)組,所以沒有容量上的限制。同時(shí),?ArrayDeque是線程不安全的,在沒有外部同步的情況下,不能再多線程環(huán)境下使用<BR>
    2022-07-07
  • Java 進(jìn)階必備之ssm框架全面整合

    Java 進(jìn)階必備之ssm框架全面整合

    SSM框架是spring MVC ,spring和mybatis框架的整合,是標(biāo)準(zhǔn)的MVC模式,將整個(gè)系統(tǒng)劃分為表現(xiàn)層,controller層,service層,DAO層四層,使用spring MVC負(fù)責(zé)請求的轉(zhuǎn)發(fā)和視圖管理,spring實(shí)現(xiàn)業(yè)務(wù)對象管理,mybatis作為數(shù)據(jù)對象的持久化引擎
    2021-10-10

最新評論

永仁县| 襄城县| 淮滨县| 民勤县| 崇明县| 高平市| 永春县| 双辽市| 军事| 个旧市| 永平县| 孟津县| 电白县| 云南省| 吉安县| 伊吾县| 收藏| 克拉玛依市| 南岸区| 崇仁县| 海原县| 巢湖市| 祥云县| 沽源县| 蓬莱市| 伊宁市| 商城县| 奇台县| 鄂伦春自治旗| 子洲县| 邵阳县| 外汇| 阿鲁科尔沁旗| 马山县| 拉孜县| 新建县| 崇左市| 巨鹿县| 阿拉善右旗| 灌云县| 永泰县|