天天看點

Java泛型中extends和super的了解Java泛型中K T V E ? object等的含義

Java泛型中K T V E ? object等的含義

E – Element (在集合中使用,因為集合中存放的是元素)

T – Type(Java 類)

K – Key(鍵)

V – Value(值)

N – Number(數值類型)

? – 表示不确定的java類型(無限制通配符類型)

S、U、V – 2nd、3rd、4th types

Object – 是所有類的根類,任何類的對象都可以設定給該Object引用變量,使用的時候可能需要類型強制轉換,但是用使用了泛型T、E等這些辨別符後,在實際用之前類型就已經确定了,不需要再進行類型強制轉換。

? 通配符類型

<? extends T> 表示類型的上界,表示參數化類型的可能是T 或是 T的子類

<? super T> 表示類型下界(Java Core中叫超類型限定),表示參數化類型是此類型的超類型(父類型),直至Object

在Java的類型擦除我們提到過:類型擦除中第一步——将所有的泛型參數用其最左邊界(最頂級的父類型)類型替換。

這裡的左邊屆可以通過extends來展現。

當生成泛型類的位元組碼時,編譯器用類型參數的擦除替換類型參數。對于無限制類型參數 (),它的擦除是 Object。對于上限類型參數(>),它的擦除是其上限(在本例中是 Comparable)的擦除。對于具有多個限制的類型參數,使用其最左限制的擦除。

extends

上界用extends關鍵字聲明,表示參數化的類型可能是所指定的類型,或者是此類型的子類。

比如,我們現在定義:

List<? extends T>

首先你很容易誤解它為繼承于T的所有類的集合,你可能認為,你定義的這個List可以用來put任何T的子類,那麼我們看一下下面的代碼:

import java.util.LinkedList;
import java.util.List;
/**
 * @author hollis
 */
public class testGeneric {
    public static void main(String[] args) {
        List<? extends Season> seasonList = new LinkedList<>();
        seasonList.add(new Spring());
    }
}
class Season{
}
class Spring extends Season{
}
           

seasonList.add(new Spring());

這行會報錯:The method put(Spring) is undefined for the type List<capture#1-of ? extends Season>

List<? extends Season> 表示 “具有任何從Season繼承類型的清單”,編譯器無法确定List所持有的類型,是以無法安全的向其中添加對象。可以添加null,因為null 可以表示任何類型。是以List 的add 方法不能添加任何有意義的元素,但是可以接受現有的子類型List 指派。

你也許試圖這樣做:

List<? extends Season> seasonList = new LinkedList<Spring>();
seasonList.add(new Spring());
           

但是,即使指明了Spring,也不能用add方法添加一個Spring對象。

list中為什麼不能加入Season類和Season類的子類呢,原因是這樣的:

List<? extends Fruit>

表示上限是Fruit,下面這樣的指派都是合法的

List<? extends Season> list1 = new ArrayList<Season>();
   List<? extends Season> list2 = new ArrayList<Spring>();
   List<? extends Season> list3 = new ArrayList<Winter>();
           

如果List<? extends Season>支援add方法的方法合法的話

list1可以add Season和所有Season的子類

list2可以add Spring和所有Spring的子類

list3可以add Winter和所有Winter的子類

這樣的話,問題就出現了

List<? extends Season>

所應該持有的對象是Season的子類,而且具體是哪一個子類還是個未知數,是以加入任何Season的子類都會有問題,

因為如果add Spring的話,可能List<? extends Season>持有的對象是new ArrayList()

Spring的加入肯定是不行的,如果 如果add Winter的話,可能List<? extends Season>持有的對象是new ArrayList<Jonathan的子類>()

Winter的加入又不合法,是以List<? extends Season> list 不能進行add

但是,這種形式還是很有用的,雖然不能使用add方法,但是可以在初始化的時候一個Season指定不同的類型。比如:

List<? extends Season> list1 = getSeasonList();//getSeasonList方法會傳回一個Season的子類的list

另外,由于我們已經保證了List中儲存的是Season類或者他的某一個子類,是以,可以用get方法直接獲得值:

List<? extends Season> seasonList = new LinkedList();
Spring spring = (Spring) seasonList.get(0);
Season season = seasonList.get(1);
           

super

下界用super進行聲明,表示參數化的類型可能是所指定的類型,或者是此類型的父類型,直至Object。

如:

List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits;
fruits.add(new Apple());                 //work
fruits.add(new RedApple());              //work
fruits.add(new Fruit());                 //compile error 
fruits.add(new Object());                //compile error
           

這裡的fruits是一個Apple的超類(父類,superclass)的List。同樣地,出于對類型安全的考慮,我們可以加入Apple對象或者其任何子類(如RedApple)對象,但由于編譯器并不知道List的内容究竟是Apple的哪個超類,是以不允許加入特定的任何超類型。

而當我們讀取的時候,編譯器在不知道是什麼類型的情況下隻能傳回Object對象,因為Object是任何Java類的最終祖先類。

PECS原則

如果要從集合中讀取類型T的資料,并且不能寫入,可以使用 ? extends 通配符;(Producer Extends)

如果要從集合中寫入類型T的資料,并且不需要讀取,可以使用 ? super 通配符;(Consumer Super)

如果既要存又要取,那麼就不要使用任何通配符。