分类: Java
2007-05-20 11:22:03
Java1.5泛型指南中文版(Java1.5 Generic Tutorial)
英文版pdf下载链接:
译者:
目 录
(第六部分)
在这一部分,我们来考虑一些通配符得高级用法。我们已经看到了上限通配符在从一个数据结构中进行读取的几个例子。现在考虑相反的情况,一个只写的数据结构。
接口Sink是这种情况的一个简单例子。
interface Sink
void flush(T t);
}
我们可以想象他被如下面的代码一样使用。方法writeAll() 被设计来把集合coll的所有元素flush到sink snk,并且返回最后一个flush的元素。
public static
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()的方法签名,如下:
String str = writeAll(cs, s); //可以调用但是返回值类型错误
这个调用现在是合法的,但是赋值产生错误,因为推断出的返回值类型是 Object因为T 匹配了Sink的类型,Object。
解决方案是使用一种我们还没有见过的有限制的通配符:有下限的通配符。语法 ? super T 表示T的一个未知的父类(或者是T自己)。这跟我们用? extends T 表示T的一个未知的子类是对应的。
String str = writeAll(cs, s); // YES!!!
使用这个语法,这个调用是合法的,推断出来的T是String,正是我们想要的。
现在让我们看一个更现实的例子。一个 java.util.TreeSet
TreeSet(Comparator<E> c)
Comparator 接口是核心:
interface Comparator<T> { int compare(T fst, T snd); }
假定我们要创建一个 TreeSet
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
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 )来给你的客户端更大的灵活性。
(原文:This reasoning applies to almost any usage of Comparable that is intended to work for arbitrary types: You always want to use Comparable super T>.
In general, if you have an API that only uses a type parameter T as an argument, its uses should take advantage of lower bounded wildcards (? super T). Conversely, if the API only returns T, you'll give your clients more flexibility by using upper bounded wildcards (? extends T). )。
现在应该很清晰,如果给定:
Set> unknownSet = new HashSet
/** 向 Set s 中添加一个元素*/
public static
这个调用是非法的:
addToSet(unknownSet, "abc"); // 非法
实际的set是一个String的set并不起作用,起作用的是传进来的表达式是一个unknown type的set,它不能保证是一个String的set或者任何其他的特定类型。
现在,考虑:
class Collections { ...
<T> public static Set<T> unmodifiableSet(Set<T> set) { ... }
}...
Set<?> s = Collections.unmodifiableSet(unknownSet); // this works! Why?
似乎这应该不被允许,但是,研究这个特定的调用,允许它是非常安全的。毕竟,unmodifiableSet 确实对任何种类的Set能工作,不管它的元素类型。
因为这种情况相对出现的次数比较多,有一个特殊的规则在能证明代码是安全的情况下允许这样的代码。()这个规则,称为wildcard capture,允许编译器推断出通配符为unknown type作为一个泛型方法的类型参数。
(原文:Because this situation arises relatively frequently, there is a special rule that allows such code under very specific circumstances in which the code can be proven to be safe. This rule, known as wildcard capture, allows the compiler to infer the unknown type of a wildcard as a type argument to a generic method.)