Java中對泛型的理解

一、泛型是什么?

1. 定義:
泛型允許你在定義類、接口或方法時使用類型參數(Type Parameter)。在使用時(如聲明變量、創建實例時),再用具體的類型實參(Type Argument)?替換這個參數。它就像是方法的形參和實參,但操作的對象是類型本身。

2. 核心目的:

  • 類型安全(Type Safety):在編譯期就能檢查類型是否正確,將運行時錯誤(ClassCastException)轉變為編譯期錯誤。

    // 沒有泛型:編譯通過,運行時報 ClassCastException
    List list = new ArrayList();
    list.add("Hello");
    Integer num = (Integer) list.get(0); // 運行時錯誤!// 有泛型:編譯期直接報錯,無法通過編譯
    List<String> list = new ArrayList<>();
    list.add("Hello");
    Integer num = list.get(0); // 編譯錯誤:不兼容的類型
  • 消除強制類型轉換(Eliminate Casts):代碼更簡潔、清晰。

    // 沒有泛型
    String str = (String) list.get(0);// 有泛型
    String str = list.get(0); // 自動知道是String,無需強轉

二、泛型擦除(Type Erasure)—— 泛型的實現原理

這是 Java 泛型的核心機制,也是很多限制的根源。

1. 是什么?
Java 的泛型是在編譯器層面實現的,而不是在運行時。在編譯后,所有的泛型類型信息都會被移除(擦除)。編譯器在生成字節碼時:

  • 將泛型類型參數替換為它的邊界(Bound)(如?T extends Number?則替換為?Number)。

  • 如果類型參數是無邊界的(如?<T>),則替換為?Object

  • 隨之插入必要的強制類型轉換,以保持類型安全。

2. 例子:

// 源代碼(編譯前)
public class Box<T> {private T value;public void set(T value) { this.value = value; }public T get() { return value; }
}Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // 無需強轉// 編譯后(概念上,字節碼級別)
public class Box { // T 被擦除private Object value; // T 被替換為 Objectpublic void set(Object value) { this.value = value; }public Object get() { return value; } // 返回Object
}Box stringBox = new Box(); //  raw type
stringBox.set("Hello");
String value = (String) stringBox.get(); // 編譯器插入了強轉!

3. 帶來的影響與限制:

  • 不能使用基本類型:如?List<int>?是錯誤的,必須用?List<Integer>。因為擦除后是?Object,而?Object?不能持有?int

  • instanceof 和 getClass():運行時無法檢測泛型類型。

    List<String> list = new ArrayList<>();
    System.out.println(list instanceof List<String>); // 編譯錯誤
    System.out.println(list instanceof List); // 正確,但只能檢查到是List,不是List<String>
  • 不能創建泛型數組new T[]?或?new List<String>[]?都是錯誤的。因為數組需要在運行時知道其確切的元素類型來保證類型安全,而擦除破壞了這個機制。

  • 不能實例化類型參數new T()?是錯誤的,因為擦除后是?new Object(),這通常不是你想要的。


三、橋接方法(Bridge Method)—— 保護多態

橋接方法是編譯器為了解決類型擦除多態沖突而自動生成的方法。

場景:?當一個類繼承或實現了一個泛型類/接口,并重寫了其中的泛型方法時。

例子:

// 泛型接口
public interface Comparable<T> {int compareTo(T other);
}// 實現類
public class String implements Comparable<String> {// 我們重寫的方法簽名:int compareTo(String other)@Overridepublic int compareTo(String other) { ... }
}

由于類型擦除,父接口?Comparable?中的方法在字節碼層面變成了?int compareTo(Object other)。這導致子類?String?實際上有兩個方法:

  1. int compareTo(String other)?(我們自己寫的)

  2. int compareTo(Object other)?(編譯器生成的橋接方法

橋接方法內部做了什么?

// 編譯器生成的橋接方法(概念上)
public int compareTo(Object other) {// 在橋接方法中,進行類型檢查和安全地向下轉型return compareTo((String) other); // 調用我們重寫的那個具體類型的方法
}

橋接方法確保了即使在類型擦除后,Java的多態機制(父類引用調用子類方法)也能正常工作,同時保證了類型安全。


四、泛型繼承和通配符(Wildcards):extends?&?super

這是泛型中最難理解但最強大的部分,通常用?PECS(Producer-Extends, Consumer-Super)?原則來概括。

1. 泛型不變性(Invariance)
首先,理解這一點至關重要:Box<String>?和?Box<Object>?沒有繼承關系,即使?String?是?Object?的子類。

Box<Object> box = new Box<String>(); // 編譯錯誤!不兼容的類型

這種特性稱為不變性(Invariance)。它保證了類型安全。如果上面成立,你就可以?box.set(new Integer(100)),從而把一個?Integer?放進一個聲明為?String?的盒子里。

2. 通配符??
為了解決需要泛型協變的需求,引入了通配符??

3. 上界通配符?? extends T?(Producer)

  • 含義:表示“未知的某種類型,但它是?T?或?T?的子類”。

  • 用途:當你主要從泛型結構中讀取數據(Producer)?時使用。

  • 規則:你可以安全地從中讀取(賦值給?T?或父類引用),但不能向其寫入(除了?null)。因為編譯器不知道具體是哪種子類,寫入可能破壞類型安全。

    List<? extends Number> numbers = new ArrayList<Integer>(); // 協變,允許
    Number num = numbers.get(0); // OK, 可以讀取為Number
    numbers.add(new Integer(100)); // 編譯錯誤!不能寫入

4. 下界通配符?? super T?(Consumer)

  • 含義:表示“未知的某種類型,但它是?T?或?T?的父類”。

  • 用途:當你主要向泛型結構中寫入數據(Consumer)?時使用。

  • 規則:你可以安全地向其寫入?T?或?T?的子類對象,但讀取出來只能賦值給?Object?引用。因為編譯器只知道容器里是?T?的父類,無法確定具體類型。

    List<? super Integer> list = new ArrayList<Number>(); // 逆變,允許
    list.add(new Integer(123)); // OK, 可以寫入Integer及其子類
    Integer num = list.get(0); // 編譯錯誤!無法安全讀取
    Object obj = list.get(0); // OK, 只能讀取為Object

5. PECS 原則總結

  • Producer-Extends (P-E):如果你需要一個提供(生產)?T?對象的泛型結構(主要調用?get()),使用?<? extends T>。例如:Collection<? extends T>.get()

  • Consumer-Super (C-S):如果你需要一個接收(消費)?T?對象的泛型結構(主要調用?add()),使用?<? super T>。例如:Collection<? super T>.add(T)

  • 既生產又消費:如果你既要讀又要寫,那就不要用通配符,直接用確切的類型,如?<T>

經典應用:Collections.copy()

public static <T> void copy(List<? super T> dest, List<? extends T> src) {// dest 是消費者 (Consumer),消費T對象,所以用 ? super T// src 是生產者 (Producer),生產T對象,所以用 ? extends Tfor (int i =0; i < src.size(); i++) {dest.set(i, src.get(i));}
}

五、常見問題總結

Q:“詳細講講Java的泛型。”

A:

“Java泛型的核心目的是提供編譯時類型安全消除強制類型轉換。它的實現機制是類型擦除,即在編譯后泛型信息會被移除,類型參數會被替換為它的邊界或Object,并由編譯器自動插入強制轉換。

類型擦除帶來了一些限制,比如不能使用基本類型、不能進行泛型的instanceof檢查、不能創建泛型數組等。為了解決擦除與多態的沖突,編譯器會生成橋接方法,它在子類重寫泛型方法時,負責進行類型檢查和安全轉型,從而保證多態性。

泛型具有不變性Box<String>?不是?Box<Object>?的子類。為了更靈活的API設計,引入了通配符???和?PECS原則

  • ? extends T?用于生產者,表示可以安全讀取,但不能寫入。

  • ? super T?用于消費者,表示可以安全寫入,但讀取受限。

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

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

相關文章

Redis開發06:使用stackexchange.redis庫結合WebAPI對redis進行增刪改查

一、接口寫法namespace WebApplication1.Controllers.Redis {[ApiController][Route("/api/[controller]")]public class RedisService : IRedisService{private readonly IConnectionMultiplexer _redis;//StackExchange.Redis庫自帶接口public RedisService(IConne…

【前端教程】從零開始學JavaScript交互:7個經典事件處理案例解析

在網頁開發中&#xff0c;交互性是提升用戶體驗的關鍵。JavaScript作為網頁交互的核心語言&#xff0c;通過事件處理機制讓靜態頁面"動"了起來。本文將通過7個經典案例&#xff0c;從簡單到復雜&#xff0c;逐步講解JavaScript事件處理的實現方法和應用場景。 案例1&…

內存模型(Memory Model)是什么?

內存模型(Memory Model)是什么? 內存模型是一個非常深刻且核心的計算機科學概念。 核心摘要 內存模型是一個契約或協議,它精確定義了: 一個線程對共享內存的寫操作,如何以及何時對其他線程可見。 內存操作(讀/寫)可以被重新排序的程度。 它連接了硬件(CPU如何執行指令…

在 MyBatis 中oracle基本數值類型的 JDBC 類型映射

基本數值類型的 JDBC 類型Java 類型JDBC 類型 (jdbcType)說明int / IntegerINTEGER標準整數類型long / LongBIGINT大整數類型short / ShortSMALLINT小整數類型float / FloatFLOAT單精度浮點數double / DoubleDOUBLE雙精度浮點數java.math.BigDecimalDECIMAL高精度小數&#xff…

Spring注解演進與自動裝配原理深度解析:從歷史發展到自定義Starter實踐

目錄 Spring注解發展史 Spring 1.X Spring 2.X Spring 2.5之前 Required Repository Aspect Spring2.5 之后 Spring 3.x ComponentScan Import 靜態導入 ImportSelector ImportBeanDefinitionRegistrar EnableXXX Spring 4.x Spring 5.x 什么是SPI 自動裝配…

第三屆機械工程與先進制造智能化技術研討會(MEAMIT2025)

【重要信息】 大會官網&#xff1a;https://www.yanfajia.com/action/p/BYE27DYDhttps://www.yanfajia.com/action/p/BYE27DYD 會議地點&#xff1a;哈爾濱理工大學 論文檢索&#xff1a;EI Compendex、Scopus 還有部份版面&#xff0c;欲投稿從速&#xff0c;即將提交出版…

筆記本電腦頻繁出現 vcomp140.dll丟失怎么辦?結合移動設備特性,提供適配性強的修復方案

對于需要用電腦處理工作的人來說&#xff0c;“vcomp140.dll 丟失” 導致程序頻繁閃退&#xff0c;無疑會嚴重影響工作效率。嘗試重啟電腦、重新安裝軟件后&#xff0c;問題依然存在&#xff0c;這時候該怎么辦&#xff1f;別著急&#xff0c;vcomp140.dll 丟失看似棘手&#x…

微動開關-電競鼠標核心!5000萬次壽命微動開關評測

一、主流電競微動開關技術對比?光磁微動技術?采用非接觸式光學觸發原理理論壽命突破5000萬次觸發響應速度0.2ms??傳統機械微動?歐姆龍D2FC-F-7N系列5000萬次標稱壽命機械結構簡單可靠??創新結構微動?雙飛燕漆藍熒光微動特殊涂層提升耐久性手感反饋獨特?二、5000萬次壽…

Go語言與Docker 開發的核心應用領域

1. 容器化應用構建與部署??輕量化鏡像構建Go 語言編譯生成靜態二進制文件&#xff0c;結合多階段構建的 Dockerfile&#xff0c;可大幅縮小鏡像體積&#xff08;例如使用 scratch 或 alpine 基礎鏡像&#xff09;&#xff0c;提升部署效率?。示例 Dockerfile 片段&#xff1…

【ESP32-IDF】網絡連接開發2:Wi?Fi 智能配網(SmartConfig)

系列文章目錄 持續更新… 文章目錄系列文章目錄前言一、Wi?Fi 智能配網概述1.SmartConfig 簡介2.SmartConfig 工作原理3.SmartConfig 協議類型二、Wi?Fi 智能配網類型定義及相關API三、Wi?Fi 智能配網示例程序總結前言 在物聯網設備開發過程中&#xff0c;如果將 Wi-Fi 的…

CVPR深度學習研究指南:特征提取模塊仍是論文創新難點

關注gongzhonghao【CVPR頂會精選】在深度學習賽道里&#xff0c;別只盯著堆模型卷參數了。最近不少高分工作都在打“可解釋”這張牌&#xff0c;把原本難以理解的黑箱模型用輕量方法剖開&#xff0c;既能增強學術價值&#xff0c;還能拓展落地場景。更妙的是&#xff0c;這類研…

redis----list詳解

列表&#xff08;List&#xff09;相當于數組或者順序表一、通用命令LPUSH key value1 [value2 ...]在列表 key 的左側&#xff08;頭部&#xff09;插入一個或多個值。示例&#xff1a;LPUSH fruits apple banana → 列表變為 [banana, apple]LPUSHX 只有列表已存在時才會執行…

【python】相機輸出圖片時保留時間戳數據

有時候需要參考時間戳&#xff0c;寫個筆記記錄下 但是輸出時間可能不穩&#xff0c;有待進一步優化 import cv2 import time import os# 創建一個保存圖像的文件夾 output_folder "camera_images" if not os.path.exists(output_folder):os.makedirs(output_folder…

(Nginx)基于Nginx+PHP 驅動 Web 應用(上):配置文件與虛擬主機篇

1.應用場景 主要用于學習基于 Nginx PHP 驅動 Web 應用&#xff08;上&#xff09;&#xff1a; 配置文件與虛擬主機篇&#xff0c;學習弄清楚Nginx的常規操作&#xff0c;之前困惑的地方。 本文主要介紹了基于NginxPHP驅動Web應用的配置方法&#xff0c;重點講解了Nginx配置…

【golang長途旅行第34站】網絡編程

網絡編程 基本介紹核心主題&#xff1a;?? Golang面向大規模后端服務程序的設計目標中&#xff0c;網絡通信是必不可少且至關重要的部分。?兩種網絡編程方式&#xff1a;???TCP Socket編程? ?性質&#xff1a;網絡編程的主流 ?底層協議&#xff1a;基于TCP/IP協議 ?舉…

Hadoop(六)

目錄&#xff1a;1.Hadoop概述2.為什么需要分布式存儲3.分布式的基礎架構分析4.HDFS的基礎架構1.Hadoop概述2.為什么需要分布式存儲3.分布式的基礎架構分析4.HDFS的基礎架構

Oracle 12g安裝

1. 下載地址 官方網站 一般這種導向的進入的都是oracle的官方網站(先登錄&#xff0c;如果沒有就創建賬號)&#xff0c;并沒有真實的12g供你下載。需要你轉入Oracle的云中下載&#xff1a;https://edelivery.oracle.com/osdc/faces/SoftwareDelivery 。我選擇的是12.1.0.2.0下…

ros2--service/服務--接口

獲取service名稱const char *get_service_name() const;std::string client_name client_->get_service_name();RCLCPP_INFO(this->get_logger(), "Client name: %s", client_name.c_str());

安卓開發---SimpleAdapter

概念&#xff1a;SimpleAdapter 是 Android 中比 ArrayAdapter 更強大的適配器&#xff0c;用于將復雜的數據綁定到復雜的布局&#xff0c;支持將 Map 中的數據映射到布局中的多個 View。方法簽名&#xff1a;public SimpleAdapter( Context context, //上下文 List<? exte…

軟考-系統架構設計師 辦公自動化系統(OAS)詳細講解

個人博客&#xff1a;blogs.wurp.top 一、OAS的核心概念與演進 1. 什么是OAS&#xff1f; OAS是一個綜合性的信息系統&#xff0c;它利用計算機技術、通信技術、系統科學和行為科學&#xff0c;為組織的日常辦公事務、信息管理和協同工作提供支持。其本質是將傳統辦公流程電…