Java泛型中的PECS原則

今天在寫代碼的時候使用到了這樣一個方法簽名:java

public void foo(Map<String, String> map);

在寫這個參數的時候正好在想一些關於泛型的東西,因而:安全

public void foo(Map<? extends String, ? extends String> map);

這兩種寫法有什麼區別呢?記得之前和同窗討論過這個問題,但後來沒有記下來,漸漸又淡忘了。今天又去翻了好多資料,總算找到一些能夠參考的,趕忙記在這裏方便之後溫故知新啦。好了,言歸正傳,上面這個問題主要涉及的是Java泛型中重要的PECS法則。那麼PECS是什麼呢?數據結構

咱們知道Java泛型能夠有多種寫法,主要是 extendssuper 關鍵字。好比:app

HashMap<T extends String>;
HashMap<? extends String>;
HashMap<T super String>;
HashMap<? super String>;

? extendside

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples; //works, apple is a subclass of Fruit.
fruits.add(new Strawberry());        //compile error

fruits是一個Fruit的子類的List,因爲Apple是Fruit的子類,所以將apples賦給fruits是合法的,可是編譯器會阻止將Strawberry加入fruits。由於編譯器只知道fruits是Fruit的某個子類的List,但並不知道到底是哪一個子類,爲了類型安全,只好阻止向其中加入任何子類。那麼可不能夠加入Fruit呢?很遺憾,也不能夠。事實上,不可以往一個使用了? extends的數據結構裏寫入任何的值。ui

可是,因爲編譯器知道它老是Fruit的子類型,所以咱們總能夠從中讀取出Fruit對象:spa

Fruit fruit = fruits.get(0);


? super對象

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的哪一個超類,所以不容許加入特定的任何超類型。get

而當咱們讀取的時候,編譯器在不知道是什麼類型的狀況下只能返回Object對象,由於Object是任何Java類的最終祖先類。編譯器


PECS原則總結

從上述兩方面的分析,總結PECS原則以下:

若是要從集合中讀取類型T的數據,而且不能寫入,可使用 ? extends 通配符;(Producer Extends)

若是要從集合中寫入類型T的數據,而且不須要讀取,可使用 ? super 通配符;(Consumer Super)

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

相關文章
相關標籤/搜索