java泛型詳解

1. extends與super

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 {

    }

extends解析

直接賦值的限制.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的自動補全返回值類型的效果。

super解析

直接賦值的限制

以泛型類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的自動補全返回值類型的效果。

PECS法則

Producer->extends    Consumer->super

若是你但願某個泛型類只能生產(方法有泛型返回值)受限定的元素(Producer),那麼就該用extends關鍵字來作限定

若是你但願某個泛型類只能消費(方法有泛型參數)受限定的元素(Consumer),那麼就該用super關鍵字來作限定

小結

extends和super的限定初看很是難懂,可是若是把泛型類的泛型參數試着實例化而且展開一下,就好理解多了。

這些奇怪的限制,主要是爲了在任何狀況下都能保證類型安全(只發生隱式的子類向父類的轉換)

 

參考資料

Java泛型中<? extends E>和<? super E>的區別

相關文章
相關標籤/搜索