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