Java泛型中extends和super的理解(轉)


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)
如果既要存又要取,那么就不要使用任何通配符。

參考資料:

Java泛型中的PECS原則

 http://www.hollischuang.com/archives/255


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM