今天在寫代碼的時候使用到了這樣一個方法簽名:java
public void foo(Map<String, String> map);
在寫這個參數的時候正好在想一些關於泛型的東西,因而:安全
public void foo(Map<? extends String, ? extends String> map);
這兩種寫法有什麼區別呢?記得之前和同窗討論過這個問題,但後來沒有記下來,漸漸又淡忘了。今天又去翻了好多資料,總算找到一些能夠參考的,趕忙記在這裏方便之後溫故知新啦。好了,言歸正傳,上面這個問題主要涉及的是Java泛型中重要的PECS法則。那麼PECS是什麼呢?數據結構
咱們知道Java泛型能夠有多種寫法,主要是 extends 和 super 關鍵字。好比: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)
若是既要存又要取,那麼就不要使用任何通配符。