日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

困扰我多年的Java泛型〈? extends T 〉和 〈? super T 〉,终于搞清楚了!

發布時間:2025/3/21 java 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 困扰我多年的Java泛型〈? extends T 〉和 〈? super T 〉,终于搞清楚了! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:JavaEdge

來源:www.jianshu.com/p/ff5f5b7e8b93

一、為什么要用通配符和邊界?

使用泛型的過程中,經常出現一種很別扭的情況。

比如我們有Fruit類,和它的派生類Apple

然后有一個最簡單的容器:Plate

盤子里可以放一個泛型的”東西

我們可以對這個東西做最簡單的“”和“”的動作:set( )get( )方法。

現定義一個“水果盤”,邏輯上水果盤當然可以裝蘋果。

但實際上Java編譯器不允許這個操作。會報錯,“裝蘋果的盤子”無法轉換成“裝水果的盤子”。

實際上,編譯器認定的邏輯是這樣的:

  • 蘋果?IS-A?水果

  • 裝蘋果的盤子?NOT-IS-A?裝水果的盤子

所以,就算容器里裝的東西之間有繼承關系,但容器之間是沒有繼承關系。

所以我們不可以把Plate<Apple>的引用傳遞給Plate<Fruit>。

為了讓泛型用起來更舒服,Sun的大師們就想出了<? extends T>和<? super T>的辦法,來讓”水果盤子“和”蘋果盤子“之間發生正當關系。

二、上界

下面就是上界通配符(Upper Bounds Wildcards)

一個能放水果以及一切是水果派生類的盤子

再直白點就是:啥水果都能放的盤子

這和我們人類的邏輯就比較接近了

Plate<? extends Fruit>和Plate<Apple>最大的區別就是:Plate<? extends Fruit>是Plate<Fruit>及Plate<Apple>的基類

直接的好處就是,我們可以用“蘋果盤”給“水果盤”賦值了。

再擴展一下,食物分成水果和肉類,水果有蘋果和香蕉,肉類有豬肉和牛肉,蘋果還有兩種青蘋果和紅蘋果。

在這個體系中,上界通配符Plate<? extends Fruit>覆蓋下圖中藍色的區域。

三、下界

相對應的下界通配符(Lower Bounds Wildcards)

表達的就是相反的概念:一個能放水果以及一切是水果基類的盤子。

Plate<? super Fruit>是Plate<Fruit>的基類,但不是Plate<Apple>的基類

對應剛才那個例子,Plate<? super Fruit>覆蓋下圖中紅色的區域。

四、上下界通配符的副作用

邊界讓Java不同泛型之間的轉換更容易了。但不要忘記,這樣的轉換也有一定的副作用。那就是容器的部分功能可能失效。

還是以剛才的Plate為例。我們可以對盤子做兩件事,往盤子里set( )新東西,以及從盤子里get( )東西。

1、上界<? extends T>不能往里存,只能往外取

<? extends Fruit>會使往盤子里放東西的set( )方法失效

但取東西get( )方法還有效

比如下面例子里兩個set()方法,插入Apple和Fruit都報錯。

編譯器只知道容器內是Fruit或者它的派生類,但具體是什么類型不知道。

可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?編譯器在看到后面用Plate<Apple>賦值以后,盤子里沒有被標上有“蘋果”。而是標上一個占位符:capture#1,來表示捕獲一個Fruit或Fruit的子類,具體是什么類不知道,代號capture#1。

然后無論是想往里插入Apple或者Meat或者Fruit編譯器都不知道能不能和這個capture#1匹配,所以就都不允許。

所以通配符<?>和類型參數<T>的區別就在于,對編譯器來說所有的T都代表同一種類型。

比如下面這個泛型方法里,三個T都指代同一個類型,要么都是String,要么都是Integer...

但通配符<?>沒有這種約束,Plate<?>單純的就表示:盤子里放了一個東西,是什么我不知道。

2、下界<? super T>不影響往里存,但往外取只能放在Object對象里

使用下界<? super Fruit>會使從盤子里取東西的get( )方法部分失效,只能存放到Object對象里。set( )方法正常。

因為下界規定了元素的最小粒度的下限,實際上是放松了容器元素的類型控制。

既然元素是Fruit的基類,那往里存粒度比Fruit小的都可以。

但往外讀取元素就費勁了,只有所有類的基類Object對象才能裝下。但這樣的話,元素的類型信息就全部丟失。

五、PECS原則

最后看一下什么是PECS(Producer Extends Consumer Super)原則,已經很好理解了。

1、頻繁往外讀取內容的,適合用上界Extends

2、經常往里插入的,適合用下界Super

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的困扰我多年的Java泛型〈? extends T 〉和 〈? super T 〉,终于搞清楚了!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。