原文出處: 併發編程網java
常常發現有List<? super T>、Set<? extends T>的聲明,是什麼意思呢?<? super T>表示包括T在內的任何T的父類,<? extends T>表示包括T在內的任何T的子類,下面咱們詳細分析一下兩種通配符具體的區別。編程
List<? extends Number> foo3的通配符聲明,意味着如下的賦值是合法的:併發
01 |
// Number "extends" Number (in this context) |
02 |
03 |
List<? extends Number> foo3 = new ArrayList<? extends Number>(); |
04 |
05 |
// Integer extends Number |
06 |
07 |
List<? extends Number> foo3 = new ArrayList<? extends Integer>(); |
08 |
09 |
// Double extends Number |
10 |
11 |
List<? extends Number> foo3 = new ArrayList<? extends Double>(); |
你不能保證讀取到Integer,由於foo3可能指向的是List<Double>。ide
你不能保證讀取到Double,由於foo3可能指向的是List<Integer>。函數
你不能插入一個Integer元素,由於foo3可能指向List<Double>。this
你不能插入一個Double元素,由於foo3可能指向List<Integer>。spa
你不能插入一個Number元素,由於foo3可能指向List<Integer>。.net
你不能往List<? extends T>中插入任何類型的對象,由於你不能保證列表實際指向的類型是什麼,你並不能保證列表中實際存儲什麼類型的對象。惟一能夠保證的是,你能夠從中讀取到T或者T的子類。code
如今考慮一下List<? super T>。對象
List<? super Integer> foo3的通配符聲明,意味着如下賦值是合法的:
01 |
// Integer is a "superclass" of Integer (in this context) |
02 |
03 |
List<? super Integer> foo3 = new ArrayList<Integer>(); |
04 |
05 |
// Number is a superclass of Integer |
06 |
07 |
List<? super Integer> foo3 = new ArrayList<Number>(); |
08 |
09 |
// Object is a superclass of Integer |
10 |
11 |
List<? super Integer> foo3 = new ArrayList<Object>(); |
你不能保證讀取到Number,由於foo3可能指向List<Object>。
惟一能夠保證的是,你能夠讀取到Object或者Object子類的對象(你並不知道具體的子類是什麼)。
你能夠插入Integer的子類的對象,由於Integer的子類同時也是Integer,緣由同上。
你不能插入Double對象,由於foo3可能指向ArrayList<Integer>。
你不能插入Number對象,由於foo3可能指向ArrayList<Integer>。
你不能插入Object對象,由於foo3可能指向ArrayList<Integer>。
請記住PECS原則:生產者(Producer)使用extends,消費者(Consumer)使用super。
若是你須要一個列表提供T類型的元素(即你想從列表中讀取T類型的元素),你須要把這個列表聲明成<? extends T>,好比List<? extends Integer>,所以你不能往該列表中添加任何元素。
若是須要一個列表使用T類型的元素(即你想把T類型的元素加入到列表中),你須要把這個列表聲明成<? super T>,好比List<? super Integer>,所以你不能保證從中讀取到的元素的類型。
若是一個列表即要生產,又要消費,你不能使用泛型通配符聲明列表,好比List<Integer>。
請參考java.util.Collections裏的copy方法(JDK1.7):
引用例子:
泛型中使用通配符有兩種形式:子類型限定<? extends xxx>和超類型限定<? super xxx>。
下面的代碼定義了一個Pair<T>類,以及Employee,Manager和President類。
如今要定義一個函數能夠打印Pair<Employee>
但是有一個問題是這個函數輸入參數只能傳遞類型Pair<Employee>,而不能傳遞Pair<Manager>和Pair<President>。例以下面的代碼會產生編譯錯誤
之因此會產生編譯錯誤,是由於Pair<Employee>和Pair<Manager>其實是兩種類型。
由上圖能夠看出,類型Pair<Manager>是類型Pair<? extends Employee>的子類型,因此爲了解決這個問題能夠把函數定義改爲
public static void printEmployeeBoddies(Pair<? extends Employee> pair)
可是使用通配符會不會致使經過Pair<? extends Employee>的引用破壞Pair<Manager>對象呢?例如:
Pair<? extends Employee> employeePair = managePair;employeePair.setFirst(new Employee("Tony", 100));
不用擔憂,編譯器會產生一個編譯錯誤。Pair<? extends Employee>參數替換後,咱們獲得以下代碼
? extends Employee getFirst()
void setFirst(? extends Employee)
對於get方法,沒問題,由於編譯器知道能夠把返回對象轉換爲一個Employee類型。可是對於set方法,編譯器沒法知道具體的類型,因此會拒絕這個調用。
超類型限定和子類型限定相反,能夠給方法提供參數,可是不能使用返回值。? super Manager這個類型限定爲Manager的全部超類。
Pair<? super Manager>參數替換後,獲得以下方法
? super Manager getFirst()
void setFirst(? super Manager)
編譯器能夠用Manager的超類型,例如Employee,Object來調用setFirst方法,可是沒法調用getFirst,由於不能把Manager的超類引用轉換爲Manager引用。
超類型限定的存在是爲了解決下面一類的問題。例如要寫一個函數從Manager[]中找出工資最高和最低的兩個,放在一個Pair中返回。
如此就能夠這樣調用