Java 泛型 ? super T 中 super 怎麼 理解與 ? extends T 有何不同?

首先說一下java泛型吧,泛型是Java SE 1.5的新特性,用來在編譯時做類型檢查的,並且會根據泛型類型自動進行類型轉換,也就是說,泛型只在編譯期起作用,主要是用來保證類型安全的,編譯後的class文件中是不會包含泛型的,這個大家可以將class文件反編譯過來看看。

<? extends T>和<? super T>表示java泛型中的上界和下界的概念,就是說<? extends T>限定的是容器中所能接收的最頂層的父類,也就是T和T的子類;<? super T> 限定的是容器中所能接收的最底層的子類,也就是T和T的父類。

舉個例子:我們有一個動物類Animal,分別有兩個子類Dog和Cat:

Class Animal {}
Class Dog {}
Class Cat {}

同時我們還有一個籠子類,裏面包含了放入和取出的方法,因爲籠子不僅僅可以放動物,也可以放其他的東西,所以我們定義成泛型T:

Class Cage<T> {
    private T t;
    public Cage (T t) {
        this.t = t;
    }
    public T get() {
        return t;
    }
    public void set(T t) {
        this.t = t;
    }
}

我們一般的思維會認爲Cage<Anamal> cage = new Cage<Dog>(new Dog),放動物的籠子肯定可以放狗吧,然而並不是,這行代碼會編譯報錯,因爲在虛擬機看來,雖然狗是一個動物,但是裝動物的籠子並不一定是裝狗的籠子。簡而言之就是說這種寫法左邊的泛型是什麼,右邊的泛型就必須定義成什麼。

接下來我們在豐富一下我們的類對象,假設所有的動物都繼承自生物,生物類有動物和植物兩個子類,狗又包含大狗和小狗,貓又包含黑貓和白貓:

Class Biology {}

Class Anamal extends Biology{}
Class Dog extends Anamal {}
Class BigDog extends Dog {}
Class SmallDog extends Dog {}
Class Cat extends Anamal {}
Class BlackCat extends Cat {}
Class whiteCat extends Cat {}

Class Plant extends Biology{}
Class Flower extends Plant {}
Class Tree extends Plant {}

那麼 <? extends Anamal> 就表示:

extends

而<? super Anamal>就表示:

super

<? extends T>會導致set方法編譯報錯:

//不能調用set方法
cage.set(new Dog());    //編譯錯誤
cage.set(new Cat());    //編譯錯誤

//調用get方法獲取到的是一個Anamal類型的對象,需要自己強轉
Anamal anamal = cage.get();
Dog dog = (Dog) cage.get();
Dog dog2 = cage.get();    //編譯錯誤

原因是因爲? extends Anamal只知道放進去的是一個Anamal或Anamal的子類,在看到後面用Cage賦值之後,會用一個佔位符:CAP#1,這個標識符不能匹配任何類,所以插入數據的時候不管插入什麼對象都會報錯,總之就是<? extends T> 下界限制符不允許插入數據。

<? super T> 允許插入數據和獲取數據,但是獲取的數據是Object類型:

Cage<? extends Anamal> cage = new Cage<Dog>(new Dog());
   
//不能調用set方法
cage.set(new Dog());    //編譯通過
cage.set(new Cat());    //編譯通過

//調用get方法獲取到的是一個Object類型的對象
Anamal anamal = cage.get(); //編譯錯誤
Dog dog2 = cage.get();    //編譯錯誤
Object o = cage.get();    //編譯通過

因爲? super T是限定了下界,只要是T的父類都可以放,這樣一來往外取數據就只能是Object類型的了,因爲Object是所有類的父類。

另外擴展說一下 PECS(Producer Extends Consumer Super)原則: 

第一、 頻繁往外讀取內容的,適合用<? extends T>;

第二、 經常往裏插入的,適合用<? super T>。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章