1 回答
TA贡献1946条经验 获得超4个赞
"? super T"和"? extends T",都是java泛型通配符,而用法又有区别,
还有super 和extends 不是java类关系中的超类和继承的意思,他是通配符的下限和上限限制.
下面看一个通配符得高级用法:
在这一部分,我们来考虑一些通配符得高级用法。我们已经看到了上限通配符在从一个数据结构中进行读取的几个例子。现在考虑相反的情况,一个只写的数据结构。
接口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 )来给你的客户端更大的灵活性。
添加回答
举报