Java泛型詳解,史上最全圖文詳解!
泛型在java中有很重要的地位,無論是開源框架還是JDK原始碼都能看到它。
毫不誇張的說,泛型是通用設計上必不可少的元素,所以真正理解與正確使用泛型,是一門必修課。
一:泛型本質
Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時型別安全檢測機制,該機制允許程式設計師在編譯時檢測到非法的型別。
泛型的本質是引數化型別,即給型別指定一個引數,然後在使用時再指定此引數具體的值,那樣這個型別就可以在使用時決定了。這種引數型別可以用在類、介面和方法中,分別被稱為泛型類、泛型介面、泛型方法。
二:為什麼使用泛型
泛型的好處是在編譯的時候檢查型別安全,並且所有的強制轉換都是自動和隱式的,提高程式碼的重用率。
(1)保證了型別的安全性。
在沒有泛型之前,從集合中讀取到的每一個物件都必須進行型別轉換,如果不小心插入了錯誤的型別物件,在執行時的轉換處理就會出錯。
比如:沒有泛型的情況下使用集合:
有泛型的情況下使用集合:
有了泛型後,定義好的集合names在編譯的時候add(123)就會編譯不透過。
相當於告訴編譯器每個集合接收的物件型別是什麼,編譯器在編譯期就會做型別檢查,告知是否插入了錯誤型別的物件,使得程式更加安全,增強了程式的健壯性。
(2) 消除強制轉換
泛型的一個附帶好處是,消除原始碼中的許多強制型別轉換,這使得程式碼更加可讀,並且減少了出錯機會。
還是舉例說明,以下沒有泛型的程式碼段需要強制轉換:
當重寫為使用泛型時,程式碼不需要強制轉換:
(3)避免了不必要的裝箱、拆箱操作,提高程式的效能
在非泛型程式設計中,將筒單型別作為Object傳遞時會引起Boxing(裝箱)和Unboxing(拆箱)操作,這兩個過程都是具有很大開銷的。引入泛型後,就不必進行Boxing和Unboxing操作了,所以執行效率相對較高,特別在對集合操作非常頻繁的系統中,這個特點帶來的效能提升更加明顯。
泛型變數固定了型別,使用的時候就已經知道是值型別還是引用型別,避免了不必要的裝箱、拆箱操作。
使用泛型之後
(4)提高了程式碼的重用性。
三:如何使用泛型
泛型有三種使用方式,分別為:泛型類、泛型介面和泛型方法。
1、泛型類
泛型類:把泛型定義在類上
定義格式:
注意事項:泛型型別必須是引用型別(非基本資料型別)
定義泛型類,在類名後新增一對尖括號,並在尖括號中填寫型別引數,引數可以有多個,多個引數使用逗號分隔:
public class GenericClass<ab,a,c> {}
當然,這個後面的引數型別也是有規範的,不能像上面一樣隨意,通常型別引數我們都使用大寫的單個字母表示:
T:任意型別 type
E:集合中元素的型別 element
K:key-value形式 key
V: key-value形式 value
示例程式碼:
泛型類:
測試類:
執行結果:
2、泛型介面
泛型方法概述:把泛型定義在方法上
定義格式:
- 注意要點:
- 方法宣告中定義的形參只能在該方法裡使用,而介面、類宣告中定義的型別形參則可以在整個介面、類中使用。當呼叫fun()方法時,根據傳入的實際物件,編譯器就會判斷出型別形參T所代表的實際型別。
注意:使用泛型的時候,前後定義的泛型型別必須保持一致,否則會出現編譯異常:
或者乾脆不指定型別,那麼 new 什麼型別都是可以的:
3、泛型方法
泛型方法,是在呼叫方法的時候指明泛型的具體型別 。
- 定義格式:
修飾符 <代表泛型的變數> 返回值型別 方法名(引數){ }
例如:
這裡可以看出,泛型方法隨著我們的傳入引數型別不同,他得到的型別也不同。泛型方法能使方法獨立於類而產生變化。
四:泛型萬用字元
Java泛型的萬用字元是用於解決泛型之間引用傳遞問題的特殊語法, 主要有以下三類:
1. 無邊界的萬用字元(Unbounded Wildcards), 就是<?>, 比如List<?>
無邊界的萬用字元的主要作用就是讓泛型能夠接受未知型別的資料.
2. 固定上邊界的萬用字元(Upper Bounded Wildcards),採用<? extends E>的形式
使用固定上邊界的萬用字元的泛型, 就能夠接受指定類及其子類型別的資料。
要宣告使用該類萬用字元, 採用<? extends E>的形式, 這裡的E就是該泛型的上邊界。
注意: 這裡雖然用的是extends關鍵字, 卻不僅限於繼承了父類E的子類, 也可以代指顯現了介面E的類
3. 固定下邊界的萬用字元(Lower Bounded Wildcards),採用<? super E>的形式
使用固定下邊界的萬用字元的泛型, 就能夠接受指定類及其父類型別的資料.。
要宣告使用該類萬用字元, 採用<? super E>的形式, 這裡的E就是該泛型的下邊界.。
注意: 你可以為一個泛型指定上邊界或下邊界, 但是不能同時指定上下邊界。
五:泛型中KTVE的含義
果點開JDK中一些泛型類的原始碼,我們會看到下面這些程式碼:
上面這些泛型類定義中的泛型引數E、K和V都是什麼意思呢?其實這些引數名稱是可以任意指定,就想方法的引數名一樣可以任意指定,但是我們通常會起一個有意義的名稱,讓別人一看就知道是什麼意思。泛型引數也一樣,E一般是指元素,用來集合類中。
常見泛型引數名稱有如下:
E: Element (在集合中使用,因為集合中存放的是元素)
T:Type(Java 類)
K: Key(鍵)
V: Value(值)
N: Number(數值型別)
?: 表示不確定的java型別
六:泛型的實現原理
泛型本質是將資料型別引數化,它透過擦除的方式來實現,即編譯器會在編譯期間「擦除」泛型語法並相應的做出一些型別轉換動作。
看一個例子就應該清楚了,例如:
我們定義了一個泛型類,定義了一個屬性成員,該成員的型別是一個泛型型別,這個 T 具體是什麼型別,我們也不知道,它只是用於限定型別的。
反編譯一下這個 Caculate 類:
發現編譯器擦除 Caculate 類後面的兩個尖括號,並且將 num 的型別定義為 Object 型別。
那麼是不是所有的泛型型別都以 Object 進行擦除呢?大部分情況下,泛型型別都會以 Object 進行替換,而有一種情況則不是。那就是使用到了extends和super語法的有界型別,如:
這種情況的泛型型別,num 會被替換為 String 而不再是 Object。
這是一個型別限定的語法,它限定 T 是 String 或者 String 的子類,也就是你構建 Caculate 例項的時候只能限定 T 為 String 或者 String 的子類,所以無論你限定 T 為什麼型別,String 都是父類,不會出現型別不匹配的問題,於是可以使用 String 進行型別擦除。
實際上編譯器會正常的將使用泛型的地方編譯並進行型別擦除,然後返回例項。但是除此之外的是,如果構建泛型例項時使用了泛型語法,那麼編譯器將標記該例項並關注該例項後續所有方法的呼叫,每次呼叫前都進行安全檢查,非指定型別的方法都不能呼叫成功。
實際上編譯器不僅關注一個泛型方法的呼叫,它還會為某些返回值為限定的泛型型別的方法進行強制型別轉換,由於型別擦除,返回值為泛型型別的方法都會擦除成 Object 型別,當這些方法被呼叫後,編譯器會額外插入一行 checkcast 指令用於強制型別轉換,這一個過程就叫做『泛型翻譯』。
七:最後
以上我就分別從Java泛型的誕生,再到泛型的使用,以及泛型的實現原理等六個方面進行了完整詳解,希望對你有所用!
關於作者:mikechen ,十餘年BAT架構經驗,資深技術專家,曾任職阿里、淘寶、百度。
歡迎關注個人公眾號: mikechen的網際網路架構,十餘年BAT架構經驗傾囊相授!
在公眾號選單欄對話方塊回覆【 架構】關鍵詞,即可檢視我 原創的300期+BAT架構技術系列文章與1000+大廠面試題答案合集。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70011997/viewspace-2855505/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java 泛型詳解Java泛型
- Java泛型詳解Java泛型
- 史上最全webview詳解WebView
- Java的泛型詳解(一)Java泛型
- Java基礎-泛型詳解Java泛型
- EventBus 3.0+ 原始碼詳解(史上最詳細圖文講解)原始碼
- 「萬字圖文」史上最姨母級Java繼承詳解Java繼承
- 微服務最全詳解(圖文全面總結)微服務
- Java註解最全詳解(超級詳細)Java
- 負載均衡最全詳解(圖文全面總結)負載
- 詳解C#泛型(三)C#泛型
- 詳解C#泛型(二)C#泛型
- 詳解C#泛型(一)C#泛型
- C#之泛型詳解C#泛型
- C# 之泛型詳解C#泛型
- java 之泛型與可變引數詳解Java泛型
- Java核心知識1:泛型機制詳解Java泛型
- 分散式儲存最全詳解(圖文全面總結)分散式
- Java記憶體模型最全詳解(5大模型圖解)Java記憶體大模型圖解
- 史上最全 Jenkins Pipeline流水線詳解Jenkins
- Java集合中List,Set以及Map等集合體系詳解(史上最全)Java
- 泛型程式設計詳解(一)泛型程式設計
- 高併發架構最全詳解(圖文全面總結)架構
- SSO單點登入最全詳解(圖文全面總結)
- DDD領域驅動最全詳解(圖文全面總結)
- 史上最全執行緒池超詳解(建議收藏)執行緒
- 擴散模型 (Diffusion Model) 之最全詳解圖解模型圖解
- 史上最全Web端即時通訊技術原理詳解Web
- ZooKeeper最全詳解(萬字圖文總結)
- java泛型詳細介紹Java泛型
- Java集合型別詳解Java型別
- Java單連結串列反轉圖文詳解Java
- 圖文詳解Java物件記憶體佈局Java物件記憶體
- PopClip使用教程圖文詳解
- Git使用教程圖文詳解Git
- MySQL 事務最全詳解MySql
- 分頁機制圖文詳解
- SpringBoot註解最全詳解(整合超詳細版本)Spring Boot