《理解 Java 泛型中的通配符:extends 與 super 的使用場景》

大家好呀!👋 今天我們要聊一個讓很多Java初學者頭疼的話題——泛型通配符。別擔心,我會用最通俗易懂的方式,帶你徹底搞懂這個看似復雜的概念。準備好了嗎?Let’s go! 🚀

一、為什么我們需要泛型通配符?🤔

首先,讓我們回憶一下泛型的基本概念。泛型就像是一個"類型參數",它讓我們可以寫出更通用的代碼。比如:

List stringList = new ArrayList<>();
List intList = new ArrayList<>();

但是,當我們想要寫一個方法,可以處理不同類型的List時,問題就來了。比如,我想寫一個打印所有List元素的方法:

public void printList(List list) {for (Object elem : list) {System.out.println(elem);}
}

這個方法看起來不錯,但實際上它不能處理ListList!😱 因為List并不是List的子類型(雖然String是Object的子類)。

這就是通配符要解決的問題!它讓我們可以更靈活地處理不同類型的泛型集合。🎯

二、通配符基礎:問號(?)的魔力 ?

通配符就是一個簡單的問號?,它表示"未知類型"。我們可以這樣改寫上面的方法:

public void printList(List list) {for (Object elem : list) {System.out.println(elem);
);
}

現在這個方法可以接受任何類型的List了!🎉 因為List表示"某種類型的List,但我不知道具體是什么類型"。

但是,通配符真正的威力在于它可以與extendssuper結合使用,這就是我們今天要深入探討的重點!🔍

三、上界通配符: 📈

3.1 基本概念

``表示"T或者T的某個子類型"。這被稱為"上界通配符"(Upper Bounded Wildcard),因為它限定了類型的上界。

舉個生活中的例子🌰:想象你有一個動物園,里面有各種動物。List可以表示"一個包含某種動物(可能是狗、貓、鳥等)的列表"。

3.2 代碼示例

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}public void processAnimals(List animals) {for (Animal animal : animals) {System.out.println("處理動物: " + animal);}
}List dogs = new ArrayList<>();
dogs.add(new Dog());
processAnimals(dogs);  // 可以正常工作!List cats = new ArrayList<>();
cats.add(new Cat());
processAnimals(cats);  // 也可以工作!

3.3 能做什么和不能做什么

可以做的事情

  1. 從集合中讀取元素(作為Animal類型)
  2. 調用Animal類的方法

不能做的事情

  1. 向集合中添加元素(除了null)

    animals.add(new Dog());  // 編譯錯誤!
    animals.add(null);       // 這是唯一允許的添加
    

    為什么?因為編譯器不知道實際的類型參數是什么。可能是List,也可能是List,所以為了類型安全,不允許添加。

3.4 實際應用場景

這種通配符特別適合"生產者"場景——即你主要從集合中讀取數據。比如:

  1. 計算集合中所有數字的總和:

    public double sumOfList(List list) {double sum = 0.0;for (Number num : list) {sum += num.doubleValue();}return sum;
    }
    
  2. 在圖形應用中處理各種形狀:

    void drawAll(List shapes) {for (Shape shape : shapes) {shape.draw();}
    }
    

四、下界通配符: 📉

4.1 基本概念

``表示"T或者T的某個父類型"。這被稱為"下界通配符"(Lower Bounded Wildcard),因為它限定了類型的下界。

繼續動物園的例子🦁:List可以表示"一個可以存放Dog及其子類的列表",比如ListList

4.2 代碼示例

public void addDogsToList(List list) {list.add(new Dog());// 也可以添加Dog的子類list.add(new Puppy());  // 假設Puppy extends Dog
}List animals = new ArrayList<>();
addDogsToList(animals);  // 可以工作List dogs = new ArrayList<>();
addDogsToList(dogs);     // 也可以工作List objects = new ArrayList<>();
addDogsToList(objects);  // 同樣可以!

4.3 能做什么和不能做什么

可以做的事情

  1. 向集合中添加T或T的子類元素
  2. 作為參數傳遞(消費場景)

不能做的事情

  1. 安全地從集合中讀取元素(除了作為Object)

    Dog dog = list.get(0);  // 編譯錯誤!
    Object obj = list.get(0);  // 這是可以的
    

    為什么?因為列表可能是List,而你不能保證取出的就是Dog。

4.4 實際應用場景

這種通配符特別適合"消費者"場景——即你主要向集合中添加數據。比如:

  1. 將多個元素添加到集合中:

    public void addNumbers(List list) {for (int i = 1; i <= 10; i++) {list.add(i);}
    }
    
  2. 在GUI應用中添加各種組件:

    void addButtons(List components) {components.add(new Button("OK"));components.add(new Button("Cancel"));
    }
    

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

現在你可能會問:“我什么時候該用extends,什么時候該用super呢?” 🤔

答案就是記住這個簡單的口訣:PECS(Producer-Extends, Consumer-Super)

  • Producer(生產者):如果你需要一個數據結構提供(生產)元素給你使用,用extends
  • Consumer(消費者):如果你需要一個數據結構接受(消費)你提供的元素,用super

5.1 PECS示例

假設我們有一個拷貝方法,從一個列表(src)拷貝到另一個列表(dest):

public static  void copy(List dest, List src) {for (T item : src) {dest.add(item);}
}

這里:

  • src是生產者(我們從中讀取數據),所以用extends
  • dest是消費者(我們向其中寫入數據),所以用super

5.2 為什么PECS有效?

這個原則之所以有效,是因為:

  1. 對于生產者(extends):

    • 你只能從中讀取,不能寫入(除了null)
    • 讀取的元素至少是某種特定類型(上界)
  2. 對于消費者(super):

    • 你可以寫入特定類型或其子類
    • 只能以Object形式讀取元素

六、無界通配符: 🌌

有時候,你只關心泛型類型本身,而不關心它的類型參數。這時可以使用無界通配符``。

6.1 基本用法

public void printListSize(List list) {System.out.println("列表大小: " + list.size());
}

這個方法可以接受任何類型的List,但你只能調用不依賴類型參數的方法(如size(), clear()等)。

6.2 與原生類型的區別

注意List和原生類型List是不同的:

  • List:這是一個知道自己是泛型但不知道具體類型的列表,是類型安全的
  • List:這是Java 5之前的原始類型,完全不知道泛型,不安全

6.3 實際應用

無界通配符常用于:

  1. 當方法實現只需要Object類提供的功能時
  2. 當類型參數不重要或不可知時
  3. 作為泛型類中非泛型方法的參數類型

七、通配符在方法簽名中的應用 🎯

通配符不僅可以用在變量聲明中,還可以用在方法簽名中,使API更加靈活。

7.1 方法參數中的通配符

// 更靈活的API設計
public void process(List numbers) { ... }// 比下面這種限制更少
public  void process(List numbers) { ... }

7.2 返回類型中的通配符

通常不建議在返回類型中使用通配符,因為這會給方法調用者帶來不便。例如:

// 不推薦
public List getNumbers() { ... }// 調用者使用起來不方便
List numbers = getNumbers();
Number num = numbers.get(0);  // 可以
Integer i = numbers.get(0);   // 編譯錯誤

八、通配符捕獲與輔助方法 🕵??♂?

有時候我們需要"捕獲"通配符的具體類型,這時可以使用輔助方法。

8.1 通配符捕獲問題

public void swap(List list, int i, int j) {Object temp = list.get(i);list.set(i, list.get(j));  // 編譯錯誤!list.set(j, temp);         // 編譯錯誤!
}

為什么出錯?因為編譯器不知道?具體是什么類型,無法保證類型安全。

8.2 使用輔助方法解決

private static  void swapHelper(List list, int i, int j) {E temp = list.get(i);list.set(i, list.get(j));list.set(j, temp);
}public void swap(List list, int i, int j) {swapHelper(list, i, j);  // 這里發生了通配符捕獲
}

編譯器可以推斷出輔助方法中的E就是通配符?的具體類型。

九、通配符與類型參數的區別 🤼

有時候看起來很相似,但它們有重要區別:

特性類型參數 ``通配符 ``
可命名是 (T)
多處使用相同類型
靈活性較低較高
適用場景需要引用類型參數只需要一次使用

9.1 何時使用哪種

  • 當需要在方法中多次引用同一類型時,使用類型參數
  • 當只需要一次使用且不需要知道具體類型時,使用通配符

十、高級話題:通配符嵌套與復雜場景 🧩

通配符可以嵌套使用,處理更復雜的場景。

10.1 嵌套通配符示例

// 一個映射,其鍵是某種類型的列表
Map> complexMap = new HashMap<>();// 一個列表,包含各種類型的列表
List> listOfLists = new ArrayList<>();

10.2 通配符與泛型方法的結合

public static  void copyWithFilter(List dest, List src, Predicate filter) {for (T elem : src) {if (filter.test(elem)) {dest.add(elem);}}
}

十一、常見誤區與陷阱 🚧

11.1 誤區1:認為ListList相同

錯!List明確知道元素是Object類型,可以安全添加Object。而List表示"不知道是什么類型",只能添加null。

11.2 誤區2:過度使用通配符

不是所有地方都需要通配符。如果類型信息重要,使用具體類型參數可能更好。

11.3 誤區3:忽略編譯器警告

當使用通配符時,如果看到編譯器警告,一定要理解原因,不要簡單地忽略或壓制它們。

十二、實戰演練:集合工具類 🛠?

讓我們實現一個簡單的集合工具類,應用所學的通配符知識。

public class CollectionUtils {// 合并兩個列表到目標列表public static  void merge(List dest,List src1, List src2) {dest.addAll(src1);dest.addAll(src2);}// 找出最大值public static > T max(List list) {if (list.isEmpty()) throw new NoSuchElementException();T max = list.get(0);for (T elem : list) {if (elem.compareTo(max) > 0) {max = elem;}}return max;}// 過濾列表public static  List filter(List list, Predicate predicate) {List result = new ArrayList<>();for (T elem : list) {if (predicate.test(elem)) {result.add(elem);}}return result;}
}

十三、總結與最佳實踐 🏆

13.1 關鍵點回顧

  1. ``:用于從結構中讀取(生產者),不能寫入(除了null)
  2. ``:用于向結構中寫入(消費者),只能以Object讀取
  3. ``:當類型完全無關緊要時使用
  4. 記住PECS原則:Producer-Extends, Consumer-Super

13.2 最佳實踐

  1. 優先使用通配符:它們使API更靈活
  2. 返回類型避免通配符:會給調用者帶來不便
  3. 通配符嵌套要謹慎:太復雜的嵌套會降低可讀性
  4. 合理使用類型參數和通配符:根據是否需要引用類型決定
  5. 測試邊界情況:特別是null值和類型邊界

十四、練習題與思考 🤔

為了鞏固所學,嘗試解決以下問題:

  1. 編寫一個方法,將一個List和一個List中的所有元素相加,返回總和
  2. 創建一個通用的addAll方法,可以將一個列表的所有元素添加到另一個列表中,考慮PECS原則
  3. 為什么Collections.max()方法的簽名是這樣的?
    public static > T max(Collection coll)
    

十五、結語 🌈

恭喜你堅持到了這里!👏 泛型通配符確實是Java中比較復雜的主題,但一旦掌握了它,你就能寫出更靈活、更安全的泛型代碼。記住,理解extendssuper的關鍵在于思考數據的流向——是生產還是消費。

剛開始可能會覺得有點繞,多練習幾次就會越來越清晰。就像學騎自行車一樣,一開始可能會摔倒幾次,但一旦掌握,就再也不會忘記了!🚴?♂?

希望這篇文章能幫你徹底理解Java泛型通配符。如果有任何問題,歡迎隨時討論!💬

Happy coding! 💻🎉

推薦閱讀文章

  • 由 Spring 靜態注入引發的一個線上T0級別事故(真的以后得避坑)

  • 如何理解 HTTP 是無狀態的,以及它與 Cookie 和 Session 之間的聯系

  • HTTP、HTTPS、Cookie 和 Session 之間的關系

  • 什么是 Cookie?簡單介紹與使用方法

  • 什么是 Session?如何應用?

  • 使用 Spring 框架構建 MVC 應用程序:初學者教程

  • 有缺陷的 Java 代碼:Java 開發人員最常犯的 10 大錯誤

  • 如何理解應用 Java 多線程與并發編程?

  • 把握Java泛型的藝術:協變、逆變與不可變性一網打盡

  • Java Spring 中常用的 @PostConstruct 注解使用總結

  • 如何理解線程安全這個概念?

  • 理解 Java 橋接方法

  • Spring 整合嵌入式 Tomcat 容器

  • Tomcat 如何加載 SpringMVC 組件

  • “在什么情況下類需要實現 Serializable,什么情況下又不需要(一)?”

  • “避免序列化災難:掌握實現 Serializable 的真相!(二)”

  • 如何自定義一個自己的 Spring Boot Starter 組件(從入門到實踐)

  • 解密 Redis:如何通過 IO 多路復用征服高并發挑戰!

  • 線程 vs 虛擬線程:深入理解及區別

  • 深度解讀 JDK 8、JDK 11、JDK 17 和 JDK 21 的區別

  • 10大程序員提升代碼優雅度的必殺技,瞬間讓你成為團隊寵兒!

  • “打破重復代碼的魔咒:使用 Function 接口在 Java 8 中實現優雅重構!”

  • Java 中消除 If-else 技巧總結

  • 線程池的核心參數配置(僅供參考)

  • 【人工智能】聊聊Transformer,深度學習的一股清流(13)

  • Java 枚舉的幾個常用技巧,你可以試著用用

  • 由 Spring 靜態注入引發的一個線上T0級別事故(真的以后得避坑)

  • 如何理解 HTTP 是無狀態的,以及它與 Cookie 和 Session 之間的聯系

  • HTTP、HTTPS、Cookie 和 Session 之間的關系

  • 使用 Spring 框架構建 MVC 應用程序:初學者教程

  • 有缺陷的 Java 代碼:Java 開發人員最常犯的 10 大錯誤

  • Java Spring 中常用的 @PostConstruct 注解使用總結

  • 線程 vs 虛擬線程:深入理解及區別

  • 深度解讀 JDK 8、JDK 11、JDK 17 和 JDK 21 的區別

  • 10大程序員提升代碼優雅度的必殺技,瞬間讓你成為團隊寵兒!

  • 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)

  • 為什么用了 @Builder 反而報錯?深入理解 Lombok 的“暗坑”與解決方案(二)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/77278.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/77278.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/77278.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

速盾:高防CDN訪問多了會影響源站嗎?

在當今數字化時代&#xff0c;內容分發網絡&#xff08;CDN&#xff09;已經成為保障網站性能和用戶體驗的重要工具。特別是高防CDN&#xff0c;它不僅能夠加速內容傳輸&#xff0c;還能有效抵御各種類型的網絡攻擊&#xff0c;確保業務的連續性和穩定性。然而&#xff0c;一些…

Unity URP Moblie AR示例工程,真機打包出來,沒陰影

效果&#xff1a; unity ar示例演示 現象&#xff1a; 真機打包測試私活沒有陰影 Unity版本&#xff1a;2022.3.4f1c1 分析原因&#xff1a; Prefab &#xff1a;ARFeatheredPlane中也有材質&#xff0c;一個用于環境遮擋&#xff0c;一個用于陰影接受。 按理說有啊。 urp …

win10下github libiec61850庫編譯調試sntp_example

libiec61850 https://github.com/mz-automation/libiec61850 v1.6 簡介 libiec61850 是一個開源&#xff08;GPLv3&#xff09;的 IEC 61850 客戶端和服務器庫實現&#xff0c;支持 MMS、GOOSE 和 SV 協議。它使用 C 語言&#xff08;根據 C99 標準&#xff09;實現&#xf…

Microsoft SQL Server Management 一鍵刪除數據庫所有外鍵

DECLARE ESQL VARCHAR(1000); DECLARE FCursor CURSOR --定義游標 FOR (SELECT ALTER TABLE O.name DROP CONSTRAINT F.name; AS CommandSQL from SYS.FOREIGN_KEYS F JOIN SYS.ALL_OBJECTS O ON F.PARENT_OBJECT_ID O.OBJECT_ID WHERE O.TYPE U AND F.TYPE …

新型多機器人協作運輸系統,輕松應對復雜路面

受到魚類、鳥類和螞蟻等微小生物體協作操縱的啟發&#xff0c;研究人員開發了多機器人協作運輸系統&#xff08;Multirobot Cooperative Transportation Systems&#xff0c;MRCTS&#xff09;運輸單個機器人無法處理的重型超大物體&#xff0c;可用于搜救行動、災難響應、軍事…

Framework Binder架構分解

整個 Binder 架構所涉及的總共有以下 5 個目錄&#xff1a; 1. /framework/base/core/java/(Java) 2. /framework/base/core/jni/ (JNI) 3&#xff0c;/framework/native/libs/binder (Native) 4&#xff0c;/framework/native/cmds/servicemanager/ (Native) 5&#xff0c…

騰訊云對象存儲以及項目業務頭像上傳

騰訊云上傳步驟&#xff1a; service-vod模塊化中 ①、參考文檔&#xff0c;引入依賴 ②、配置文件application.properties ③、創建工具類 初始化bean的時候讀取配置文件 Component public class ConstantPropertiesUtil implements InitializingBean{Value("${t…

LeetCode hot 100—括號生成

題目 數字 n 代表生成括號的對數&#xff0c;請你設計一個函數&#xff0c;用于能夠生成所有可能的并且 有效的 括號組合。 示例 示例 1&#xff1a; 輸入&#xff1a;n 3 輸出&#xff1a;["((()))","(()())","(())()","()(())",&…

SpringBoot企業級開發之【文章分類-新增文章分類】

看一下新增文章的需求&#xff1a; 接口文檔&#xff1a; 開發思路&#xff1a; 先在controller下去創建add方法&#xff0c;方法內導入Service類獲取add的結果&#xff1b;再在Service接口下去創建add的方法&#xff1b;然后在Service實現類下去實現方法的作用&#xff0c;且導…

Minecraft盔甲機制詳解(1.9之后)

Minecraft的盔甲有很多種&#xff0c;但是評判盔甲的好壞&#xff0c;通常玩家會使用一個變量來評判——護甲值 護甲值的機制很簡單&#xff0c;一格護甲值 &#xff08;半個灰色的衣服圖標&#xff09;最多能提供4%的防御 護甲值在不開作弊的生存模式理論上限是20點&#xf…

為什么要給單片機植入操作系統

給單片機植入操作系統&#xff08;通常是實時操作系統&#xff0c;RTOS&#xff09;主要是為了在資源有限的環境中實現更高效、更可靠的多任務管理和系統調度。以下是主要原因和優勢&#xff1a; 1. 多任務并行處理 背景&#xff1a;單片機通常需要同時處理多個任務&#xff0…

Arduino+ESP826601s模塊連接阿里云并實現溫濕度數據上報

ArduinoESP826601s模塊連接阿里云并實現溫濕度數據上報 一、前言二、準備工作三、程序代碼1. Arduino的程序2. ESP826601的程序3. 上面程序需要注意的地方 四、運行結果五、結束語 一、前言 看完我這三篇文章&#xff0c;相信各位朋友對于阿里云物聯網平臺的使用都有了一定的認…

Java 工廠設計模式詳解:用統一入口打造靈活可擴展的登錄系統----掌握 Spring 源碼的基礎第一步

一、前言 在實際開發中&#xff0c;我們經常面臨以下場景&#xff1a; 系統支持多種登錄方式&#xff08;用戶名密碼、管理員登錄、OAuth 登錄、短信登錄等&#xff09; 每種登錄方式的認證邏輯不同 我們希望對外提供一個統一的接口調用&#xff0c;而不暴露具體實現 這個…

Windows Acrobat Pro DC-v2025.001.20435-x64-CN-Portable便攜版

Windows Acrobat Pro 鏈接&#xff1a;https://pan.xunlei.com/s/VOO1nMjQ1Qf53dyISGne0c_9A1?pwdsfgn# Acrobat Pro 2024 專業增強版特色 ● 創建和編輯 PDF 文件&#xff1a;可以將各種類型的文檔轉換為 PDF 格式&#xff0c;并進行編輯和修改。 ● 合并和拆分 PDF&#…

【2025“華中杯”大學生數學建模挑戰賽】C題:就業狀態分析與預測 詳細解題思路

目錄 2025“華中杯”大學生數學建模挑戰賽C題 詳細解題思路一、問題一1.1 問題分析1.2 數學模型 1.3 Python代碼1.4 Matlab代碼 二、問題二2.1 問題分析2.2 數學模型 2.3 Python代碼2.4 Matlab代碼 三、問題三3.1 問題分析 四、問題四4.1 問題分析與數學模型 2025“華中杯”大學…

識別法院PDF文件特定字段并插入數據庫【正則表達式+本地化部署】

pdf解析法院協助單特定字段&#xff0c;開源項目&#xff0c;結合若依項目進行開發&#xff0c;不連互聯網&#xff0c;本地開發部署&#xff0c;前端使用vue3技術,后端用若依分離版spring botot技術&#xff0c;實現將pdf法院協助執行通知書中的特定字段如&#xff1a;時間、文…

擁抱健康養生,開啟活力生活

在快節奏的現代生活中&#xff0c;人們愈發重視健康養生。它并非高深莫測的學問&#xff0c;而是融合于日常點滴&#xff0c;對我們的生活有著深遠影響。 合理飲食是健康養生的基石。一日三餐&#xff0c;應遵循營養均衡原則。多攝入蔬菜、水果&#xff0c;它們富含維生素與膳食…

無人機姿態穩定與動態控制模塊概述!

一、設計難點 1. 動態算力需求與硬件能力的不匹配** 無人機邊緣計算設備通常受限于體積和重量&#xff0c;導致其計算單元&#xff08;如CPU、GPU&#xff09;的算力有限&#xff0c;難以應對突發的高負載任務&#xff08;如實時圖像處理、AI推理&#xff09;。 挑戰&am…

MySQL 臨時表介紹

在 MySQL 數據庫中&#xff0c;臨時表是一種特殊類型的表&#xff0c;它在數據庫會話期間存在&#xff0c;會話結束時自動刪除。臨時表為處理特定的、臨時性的數據操作任務提供了一種高效且便捷的方式。 一、臨時表的創建 使用CREATE TEMPORARY TABLE語句來創建臨時表。其語法…

量子糾纏物理本質、技術實現、應用場景及前沿研究

以下是關于 量子糾纏(Quantum Entanglement) 的深度解析,涵蓋物理本質、技術實現、應用場景及前沿研究,以技術視角展開: 一、量子糾纏的物理本質 1. 核心定義 量子糾纏是多個量子系統(如粒子)間的一種關聯狀態,表現為: 非局域性:糾纏態粒子無論相距多遠,測量其中一…