java中super()_Java 泛型中 extends 和 super 的区别是什么?
<? extends T> 和 <? super T> 是 Java 泛型中的 “通配符(Wildcards)” 和 “邊界(Bounds)” 的概念。
<? extends T>:是指 “上界通配符(Upper Bounds Wildcards)”。
<? super T>:是指 “下界通配符(Lower Bounds Wildcards)”。
為什么要用通配符和邊界?
使用泛型的過程中,經常出現一種很別扭的情況。比如按照題主的例子,我們有 Fruit 類,和它的派生類 Apple 類。
class Fruit {} class Apple extends Fruit {}然后有一個最簡單的容器:Plate 類。盤子里可以放一個泛型的“東西”。我們可以對這個東西做最簡單的“放”和“取”的動作:set() 和 get() 方法。
class Plate<T>{private T item;public Plate(T t){item=t;}public void set(T t){item=t;}public T get(){return item;} }現在我定義一個“水果盤子”,邏輯上水果盤子當然可以裝蘋果。
Plate<Fruit> p=new Plate<Apple>(new Apple());但實際上 Java 編譯器不允許這個操作。會報錯,“裝蘋果的盤子”無法轉換成“裝水果的盤子”。
error: incompatible types: Plate<Apple> cannot be converted to Plate<Fruit>所以我的尷尬癥就犯了。實際上,編譯器腦袋里認定的邏輯是這樣的:
蘋果 IS-A 水果
裝蘋果的盤子 NOT-IS-A 裝水果的盤子
所以,就算容器里裝的東西之間有繼承關系,但容器之間是沒有繼承關系的。所以我們不可以把 Plate 的引用傳遞給 Plate。
為了讓泛型用起來更舒服,Sun 的大腦袋們就想出了 <? extends T> 和 <? super T> 的辦法,來讓”水果盤子“和”蘋果盤子“之間發生關系。
什么是上界?
下面代碼就是“上界通配符(Upper Bounds Wildcards)”:
Plate<?extends Fruit>如果把 Fruit 和 Apple 的例子再擴展一下,食物分成水果和肉類,水果有蘋果和香蕉,肉類有豬肉和牛肉,蘋果還有兩種青蘋果和紅蘋果。
//Lev 1classFood{}//Lev 2classFruitextendsFood{}classMeatextendsFood{}//Lev 3 classAppleextendsFruit{} classBananaextendsFruit{} classPorkextendsMeat{} classBeefextendsMeat{}//Lev 4 classRedAppleextendsApple{} classGreenAppleextendsApple{}在這個體系中,下界通配符 Plate<?extends Fruit> 覆蓋下圖中藍色的區域。
什么是下界?
相對應的,“下界通配符(Lower Bounds Wildcards)”:
1Plate<? super Fruit>表達的就是相反的概念:一個能放水果以及一切是水果基類的盤子。Plate<?super Fruit> 是 Plate<Fruit> 的基類,但不是 Plate<Apple> 的基類。對應剛才那個例子,Plate<?super Fruit> 覆蓋下圖中紅色的區域。
上下界通配符的副作用
邊界讓 Java 不同泛型之間的轉換更容易了。但不要忘記,這樣的轉換也有一定的副作用。那就是容器的部分功能可能失效。
還是以剛才的 Plate 為例。我們可以對盤子做兩件事,往盤子里 set() 新東西,以及從盤子里 get() 東西。
class Plate<T>{private T item;public Plate(T t){item=t;}public void set(T t){item=t;}public T get(){return item;} }上界 <? extends T> 不能往里存,只能往外取。
<? extends Fruit> 會使往盤子里放東西的 set() 方法失效。但取東西 get() 方法還有效。比如下面例子里兩個 set() 方法,插入 Apple 和 Fruit 都報錯。
Plate<? extends Fruit> p=new Plate<Apple>(new Apple());//不能存入任何元素p.set(new Fruit()); //Errorp.set(new Apple()); //Error//讀取出來的東西只能存放在Fruit或它的基類里。Fruit newFruit1=p.get();Object newFruit2=p.get(); Apple newFruit3=p.get(); //Error原因是編譯器只知道容器內是 Fruit 或者它的派生類,但具體是什么類型不知道。可能是 Fruit?可能是 Apple?也可能是 Banana,RedApple,GreenApple?編譯器在看到后面用 Plate 賦值以后,盤子里沒有被標上有“蘋果”。而是標上一個占位符:CAP#1,來表示捕獲一個 Fruit 或 Fruit 的子類,具體是什么類不知道,代號 CAP#1。然后無論是想往里插入 Apple 或者 Meat 或者 Fruit 編譯器都不知道能不能和這個 CAP#1 匹配,所以就都不允許。
所以通配符 <?> 和類型參數的區別就在于,對編譯器來說所有的 T 都代表同一種類型。比如下面這個泛型方法里,三個 T 都指代同一個類型,要么都是 String,要么都是 Integer。
public <T> List<T> fill(T... t);但通配符 <?> 沒有這種約束,Plate<?> 單純的就表示:盤子里放了一個東西,是什么我不知道。
所以題主問題里的錯誤就在這里,Plate<?extends Fruit> 里什么都放不進去。
下界 <? super T> 不影響往里存,但往外取只能放在 Object 對象里。
使用下界 <? super Fruit> 會使從盤子里取東西的 get() 方法部分失效,只能存放到 Object 對象里。set() 方法正常。
Plate<? super Fruit> p=new Plate<Fruit>(new Fruit());//存入元素正常p.set(new Fruit());p.set(new Apple());//讀取出來的東西只能存放在Object類里。Apple newFruit3=p.get(); //ErrorFruit newFruit1=p.get(); //Error Object newFruit2=p.get();因為下界規定了元素的最小粒度的下限,實際上是放松了容器元素的類型控制。既然元素是 Fruit 的基類,那往里存粒度比 Fruit 小的都可以。但往外讀取元素就費勁了,只有所有類的基類 Object 對象才能裝下。但這樣的話,元素的類型信息就全部丟失。
PECS 原則
最后看一下什么是 PECS(Producer Extends Consumer Super)原則,已經很好理解了:
- 頻繁往外讀取內容的,適合用上界 Extends。
- 經常往里插入的,適合用下界 Super。
總結
以上是生活随笔為你收集整理的java中super()_Java 泛型中 extends 和 super 的区别是什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue 打印出git提交信息_规范git
- 下一篇: go 定义一个结构体并赋初始值_Go中必