extends關鍵字限制了泛型類可使用的泛型參數類型的上限(Upper Bound)安全
super關鍵字限制了泛型類可使用的泛型參數類型的下限(Lower Bound)ui
先定義幾個簡單的Classspa
class Food { } class Fruit extends Food { } class Meat extends Food { } class Apple extends Fruit { } class Orange extends Fruit { } class Pork extends Meat { } class Beef extends Meat { }
直接賦值的限制.net
以泛型類List爲例,若是咱們聲明下面這樣的一個Listcode
List<? extends Fruit> fruitList;
那麼後續給這個fruitList賦值時,這個值容許使用的泛型參數的上限就是Fruit,也就是Fruit或者Fruit的子類。因此下面的代碼是能夠編譯經過的blog
fruitList = new ArrayList<Apple>(); fruitList = new ArrayList<Orange>();
下面的代碼則會引起編譯失敗get
fruitList = new ArrayList<Food>();//Food不是Fruit的子類,編譯失敗
泛型方法參數的限制編譯器
在上面的例子中,fruitList的含有泛型參數的方法,例如add,是受到極大的限制的:it
fruitList.add(new Object());//編譯錯誤 fruitList.add(new Food());//編譯錯誤 fruitList.add(new Fruit());//編譯錯誤 fruitList.add(new Apple());//編譯錯誤 fruitList.add(null);//編譯經過
能夠發現,除了不帶有Class類型信息的null,任何Class(甚至包括Object)的實例,都不能做爲add方法的泛型參數,這是爲何呢?編譯
由於extends關鍵字只是限制了泛型類可使用的泛型參數的上限,編譯器並不知道fruitList實際指向的實例使用的泛型參數具體是什麼,多是Fruit,也多是Apple或者Orange。
這句話不太好理解,可是若是把fruitList直接替換成已經正確設置了泛型參數的泛型類的實例,而後調用泛型方法,可能會比較好懂。
例如在泛型參數爲Orange時,上面的代碼能夠轉換爲下面的形式:
new ArrayList<Orange>().add(new Object());//編譯錯誤 new ArrayList<Orange>().add(new Food());//編譯錯誤 new ArrayList<Orange>().add(new Fruit());//編譯錯誤 new ArrayList<Orange>().add(new Apple());//編譯錯誤 new ArrayList<Orange>().add(null);//編譯經過
也就是說,編譯器採用了很是保守的策略來確保類型安全(只容許發生隱式的從子類向父類的類型轉換)。
泛型方法返回值的限制
在上面的例子中,因爲能夠肯定fruitList對應的實例使用的泛型參數必然是Fruit或者Fruit的子類,因此若是有方法的返回值是泛型參數,那麼能夠肯定這個返回值必然也是Fruit或者Fruit的子類了
Fruit fruit = fruitList.get(0);
上面這行代碼是IDEA的自動補全返回值類型的效果。
直接賦值的限制
以泛型類List爲例,若是咱們聲明下面這樣的一個List
List<? super Fruit> fruitList;
那麼後續給這個fruitList賦值時,這個值容許使用的泛型參數的下限就是Fruit,也就是Fruit或者Fruit的父類。因此下面的代碼是能夠編譯經過的
fruitList = new ArrayList<Fruit>();//編譯經過 fruitList = new ArrayList<Food>();//編譯經過
而下面的代碼則會引起編譯失敗
fruitList = new ArrayList<Apple>();//編譯錯誤
泛型方法參數的限制
在上面的例子中,fruitList的含有泛型參數的方法,例如add,也受到了必定的限制:
fruitList.add(new Object());//編譯錯誤 fruitList.add(new Food());//編譯錯誤 fruitList.add(new Fruit());//編譯經過 fruitList.add(new Apple());//編譯經過 fruitList.add(null);//編譯經過
能夠發現,只容許使用Fruit或者Fruit的子類的實例做爲參數,這又是爲啥呢?
由於super關鍵字是限制了泛型類可使用的泛型參數的下限,泛型參數不管是什麼,都必然是Fruit或者Fruit的父類,這必然也是Fruit的子類的父類了。
因此只要給泛型方法傳入Fruit或者Fruit子類的實例做爲參數,最多隻會引起隱式類型轉換,不會致使異常。
以泛型參數爲Food爲例,把上面的代碼轉換一下:
new ArrayList<Food>().add(new Object());//編譯錯誤 new ArrayList<Food>().add(new Food());//編譯經過,可是若是換另一種父類就出錯了 new ArrayList<Food>().add(new Fruit());//編譯經過 new ArrayList<Food>().add(new Apple());//編譯經過 new ArrayList<Food>().add(null);//編譯經過
這也是編譯器爲了確保類型安全做出的努力
泛型方法返回值的限制
在上面的例子中,因爲沒法肯定fruitList對應的實例使用的泛型參數究竟是什麼玩意(多是Fruit,也多是Food,甚至多是Object),因此只能用萬能的Object做爲返回值類型了:
Object object = fruitList.get(0);
上面這行代碼是IDEA的自動補全返回值類型的效果。
Producer->extends Consumer->super
若是你但願某個泛型類只能生產(方法有泛型返回值)受限定的元素(Producer),那麼就該用extends關鍵字來作限定
若是你但願某個泛型類只能消費(方法有泛型參數)受限定的元素(Consumer),那麼就該用super關鍵字來作限定
extends和super的限定初看很是難懂,可是若是把泛型類的泛型參數試着實例化而且展開一下,就好理解多了。
這些奇怪的限制,主要是爲了在任何狀況下都能保證類型安全(只發生隱式的子類向父類的轉換)