通配符與嵌套
上界通配符<? extends T>
咱們先來看一個例子:web
class Fruit {}
class Apple extends Fruit {}
如今咱們定義一個盤子類:微信
class Plate<T>{
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());
你會發現這段代碼沒法進行編譯。裝蘋果的盤子」沒法轉換成「裝水果的盤子:ide
cannot convert from Plate<Apple> to Plate<Fruit>
從上面代碼咱們知道,就算容器中的類型之間存在繼承關係,可是Plate和Plate兩個容器之間是不存在繼承關係的。在這種狀況下,Java就設計成Plate<? extend Fruit>來讓兩個容器之間存在繼承關係。咱們上面的代碼就能夠進行賦值了ui
Plate<? extends Fruit> p=new Plate<Apple>(new Apple());
Plate<? extend Fruit>是Plate< Fruit >和Plate< Apple >的基類。咱們經過一個更加詳細的例子來看一下上界的界限:spa
class Food{}
class Fruit extends Food {}
class Meat extends Food {}
class Apple extends Fruit {}
class Banana extends Fruit {}
class Pork extends Meat{}
class Beef extends Meat{}
class RedApple extends Apple {}
class GreenApple extends Apple {}
在上面這個類層次中,Plate<? extend Fruit>,覆蓋下面的藍色部分:.net
若是咱們往盤子裏面添加數據,例如:設計
p.set(new Fruit());
p.set(new Apple());
你會發現沒法往裏面設置數據,按道理說咱們將泛型類型設置爲? extend Fruit。按理說咱們往裏面添加Fruit的子類應該是能夠的。可是Java編譯器不容許這樣操做。<? extends Fruit>會使往盤子裏放東西的set()方法失效。但取東西get()方法還有效code
緣由是:Java編譯期只知道容器裏面存放的是Fruit和它的派生類,具體是什麼類型不知道,多是Fruit?多是Apple?也多是Banana,RedApple,GreenApple?編譯器在後面看到Plate< Apple >賦值之後,盤子裏面沒有標記爲「蘋果」。只是標記了一個佔位符「CAP#1」,來表示捕獲一個Fruit或者Fruit的派生類,具體是什麼類型不知道。全部調用代碼不管往容器裏面插入Apple或者Meat或者Fruit編譯器都不知道能不能和這個「CAP#1」匹配,因此這些操做都不容許。orm
最新理解:一個Plate的引用,指向的多是一個Plate類型的盤子,要往這個盤子裏放Banana固然是不被容許的。個人一個理解是:Plate表明某個只能放某種類型水果的盤子,而不是什麼水果都能往裏放的盤子
可是上界通配符是容許讀取操做的。例如代碼:
Fruit fruit=p.get();
Object object=p.get();
這個咱們很好理解,因爲上界通配符設定容器中只能存放Fruit及其派生類,那麼獲取出來的咱們均可以隱式的轉爲其基類(或者Object基類)。因此上界描述符Extends適合頻繁讀取的場景。
下界通配符<? super T>
下界通配符的意思是容器中只能存放T及其T的基類類型的數據。咱們仍是以上面類層次的來看,<? super Fruit>覆蓋下面的紅色部分:
下界通配符<? super T>不影響往裏面存儲,可是讀取出來的數據只能是Object類型。緣由是:下界通配符規定了元素最小的粒度,必須是T及其基類,那麼我往裏面存儲T及其派生類都是能夠的,由於它均可以隱式的轉化爲T類型。可是往外讀就很差控制了,裏面存儲的都是T及其基類,沒法轉型爲任何一種類型,只有Object基類才能裝下。
PECS原則
最後簡單介紹下Effective Java這本書裏面介紹的PECS原則。
上界不能往裏存,只能往外取,適合頻繁往外面讀取內容的場景。下界不影響往裏存,但往外取只能放在Object對象裏,適合常常往裏面插入數據的場景。
<?>無限通配符
無界通配符 意味着能夠使用任何對象,所以使用它相似於使用原生類型。但它是有做用的,原生類型能夠持有任何類型,而無界通配符修飾的容器持有的是某種具體的類型。舉個例子,在List<\?>類型的引用中,不能向其中添加Object, 而List類型的引用就能夠添加Object類型的變量。
最後提醒一下的就是,List<\object>與List並不等同,List<\object>是List的子類。還有不能往List list裏添加任意對象,除了null。
夯實基礎,關注前沿,娛樂生活
掌握更多前沿技術,獲取更多笑點
請關注--------喘口仙氣
本文分享自微信公衆號 - 喘口仙氣(gh_db8538619cdd)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。