1 回答

TA貢獻1946條經驗 獲得超4個贊
"? super T"和"? extends T",都是java泛型通配符,而用法又有區別,
還有super 和extends 不是java類關系中的超類和繼承的意思,他是通配符的下限和上限限制.
下面看一個通配符得高級用法:
在這一部分,我們來考慮一些通配符得高級用法。我們已經看到了上限通配符在從一個數據結構中進行讀取的幾個例子?,F在考慮相反的情況,一個只寫的數據結構。
接口Sink是這種情況的一個簡單例子。
interface Sink<T> {
void flush(T t);
}
我們可以想象他被如下面的代碼一樣使用。方法writeAll() 被設計來把集合coll的所有元素flush到sink snk,并且返回最后一個flush的元素。
public static <T> T writeAll(Collection<T> coll, Sink<T> snk) {
T last = null;
for (T t : coll) {
last = t;
snk.flush(last);
}
return last;
}
Sink<Object> s;
Collection<String> cs;
String str = writeAll(cs, s); // 非法的調用!!
像上面所寫,writeAll() 的調用是非法的,因為沒有有效的類型參數可以被推斷出來。String 或 Object都不是T的合適的類型,因為Collection的元素和 Sink的元素必須是同樣的類型。
我們可以解決這個問題,通過使用通配符來修改writeAll()的方法簽名,如下:
<T> T writeAll(Collection<? extends T> coll, Sink<T> snk)
String str = writeAll(cs, s); //可以調用但是返回值類型錯誤
這個調用現在是合法的,但是賦值產生錯誤,因為推斷出的返回值類型是 Object因為T 匹配了Sink的類型,Object。
解決方案是使用一種我們還沒有見過的有限制的通配符:有下限的通配符。語法 ? super T 表示T的一個未知的父類(或者是T自己)。這跟我們用? extends T 表示T的一個未知的子類是對應的。
<T> T writeAll(Collection<T> coll, Sink<? super T> snk)
String str = writeAll(cs, s); // YES!!!
使用這個語法,這個調用是合法的,推斷出來的T是String,正是我們想要的。
現在讓我們看一個更現實的例子。一個 java.util.TreeSet<E> 代表一個有序的元素是E類型的樹。創建一個TreeSet的一個方法是傳遞一個 Comparator 對象給構造函數。這個Comparator將會用來按照需要對TreeSet進行排序。
TreeSet(Comparator<E> c)
Comparator 接口是核心:
interface Comparator<T>
假定我們要創建一個 TreeSet<String> 并傳遞一個合適的 Comparator,我們需要傳一個能比較String的Comparator。這可以是一個 Comparator<String>,也可以是一個 Comparator<Object>。然而我們不能用Comparator<Object>來調用上面的構造函數。我們可以使用一個有下限的通配符來得到我們需要的靈活性:
TreeSet(Comparator<? super E> c)
這允許任何可用的Comparator被傳遞進去。
作為使用下限通配符最終的例子,讓我們來看看方法 Collections.max(),它返回一個集合中的最大的元素。
現在,為了讓max()能工作,傳進來的集合中的所有元素必須實現 Comparatable接口。而且,他們必須都能夠被彼此比較(all be comparable to each other)。第一個嘗試是:
public static <T extends Comparable<T>> T max(Collection<T> coll)
就是說,方法的參數是某一個能和自己進行比較的T的集合。這限制太嚴格了。
為什么?考慮一個能和任何對象進行比較的類型:
class Foo implements Comparable<Object> ...
Collection<Foo> cf = ...;
Collections.max(cf); // 應該能工作
cf 中的每個元素都可以和每個cf中的其他元素進行比較,因為每個這樣的元素都是一個Foo,它可以和任意的對象進行比較,也可以和另一個Foo進行比較。
但是,使用上面的方法簽名,我們發現這個調用被拒絕。推斷出來的類型必須是Foo,但是Foo沒有實現接口 Comparable<Foo>。
T 精確的(exactly)和自己能比較是不需要的。所需要的是 T能夠和它的父類中的一個進行比較,這導出:(注:Collections.max()的實際方法簽名更復雜,我們在第10部分再討論。)
public static <T extends Comparable<? super T>> T max(Collection<T> coll)
這個推論對大多數想讓 Comparable 對任意類型生效的用法中都有效:你總是應該使用 Comparable<? super T>。
總之,如果你有一個只使用類型參數T作為參數的API,它的使用應該利用下限通配符( ? super T )的好處。相反的,如果API只返回T,你應該使用上限通配符( ? extends T )來給你的客戶端更大的靈活性。
添加回答
舉報