The Java Tutorials have been written for JDK 8.Java教程是为JDK 8编写的。Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available.本页中描述的示例和实践没有利用后续版本中引入的改进,并且可能使用不再可用的技术。See Java Language Changes for a summary of updated language features in Java SE 9 and subsequent releases.有关Java SE 9及其后续版本中更新的语言特性的摘要,请参阅Java语言更改。
See JDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases.有关所有JDK版本的新功能、增强功能以及已删除或不推荐的选项的信息,请参阅JDK发行说明。
In this section, we'll consider some of the more advanced uses of wildcards. 在本节中,我们将考虑通配符的一些更高级的用途。We've seen several examples where bounded wildcards were useful when reading from a data structure. 我们已经看到了几个例子,其中有界通配符在读取数据结构时非常有用。Now consider the inverse, a write-only data structure. 现在考虑逆,一个只写数据结构。The interface 接口Sink
is a simple example of this sort.Sink
就是这样一个简单的例子。
interface Sink<T> { flush(T t); }
We can imagine using it as demonstrated by the code below. 我们可以想象使用它,如下面的代码所示。The method writeAll()
is designed to flush all elements of the collection coll
to the sink snk
, and return the last element flushed.writeAll()
方法用于将集合coll
的所有元素刷新到sink snk
,并返回最后刷新的元素。
public static <T> T writeAll(Collection<T> coll, Sink<T> snk) { T last; for (T t : coll) { last = t; snk.flush(last); } return last; } ... Sink<Object> s; Collection<String> cs; String str = writeAll(cs, s); // Illegal call.
As written, the call to 如前所述,对writeAll()
is illegal, as no valid type argument can be inferred; neither String
nor Object
are appropriate types for T
, because the Collection
element and the Sink
element must be of the same type.writeAll()
的调用是非法的,因为无法推断有效的类型参数;String
和Object
都不是T
的合适类型,因为Collection
元素和Sink
元素必须是同一类型。
We can fix this error by modifying the signature of 我们可以通过使用通配符修改writeAll()
as shown below, using a wildcard.writeAll()
的签名来修复此错误,如下所示。
public static <T> T writeAll(Collection<? extends T>, Sink<T>) {...} ... // Call is OK, but wrong return type. String str = writeAll(cs, s);
The call is now legal, but the assignment is erroneous, since the return type inferred is 该调用现在是合法的,但赋值是错误的,因为推断的返回类型是Object
because T
matches the element type of s
, which is Object
.Object
,因为T
匹配s
的元素类型,即Object
。
The solution is to use a form of bounded wildcard we haven't seen yet: wildcards with a lower bound. 解决方案是使用一种我们还没有见过的有界通配符:带下限的通配符。The syntax 语法? super T
denotes an unknown type that is a supertype of T
(or T
itself; remember that the supertype relation is reflexive). ? super T
表示一个未知类型,它是T
的超类型(或T
本身;请记住,超类型关系是自反的)。It is the dual of the bounded wildcards we've been using, where we use 这是我们一直在使用的有界通配符的对偶,我们在哪里使用? extends T
to denote an unknown type that is a subtype of T
.? extends T
以表示作为T
的子类型的未知类型。
public static <T> T writeAll(Collection<T> coll, Sink<? super T> snk) { ... } String str = writeAll(cs, s); // Yes!
Using this syntax, the call is legal, and the inferred type is 使用这种语法,调用是合法的,推断的类型是String
, as desired.String
(根据需要)。
Now let's turn to a more realistic example. 现在让我们看一个更现实的例子。A java.util.TreeSet<E>
represents a tree of elements of type E
that are ordered. One way to construct a TreeSet
is to pass a Comparator
object to the constructor. That comparator will be used to sort the elements of the TreeSet
according to a desired ordering.
TreeSet(Comparator<E> c)
The Comparator
interface is essentially:
interface Comparator<T> { int compare(T fst, T snd); }
Suppose we want to create a TreeSet<String>
and pass in a suitable comparator, We need to pass it a Comparator
that can compare String
s. This can be done by a Comparator<String>
, but a Comparator<Object>
will do just as well. However, we won't be able to invoke the constructor given above on a Comparator<Object>
. We can use a lower bounded wildcard to get the flexibility we want:
TreeSet(Comparator<? super E> c)
This code allows any applicable comparator to be used.该代码允许使用任何适用的比较器。
As a final example of using lower bounded wildcards, lets look at the method 作为使用下限通配符的最后一个示例,让我们看看方法Collections.max()
, which returns the maximal element in a collection passed to it as an argument. Collections.max()
,它返回作为参数传递给它的集合中的最大元素。Now, in order for 现在,为了让max()
to work, all elements of the collection being passed in must implement Comparable
. max()
工作,传入的集合的所有元素都必须实现Comparable
。Furthermore, they must all be comparable to each other.此外,它们必须相互比较。
A first attempt at generifying this method signature yields:首次尝试将此方法签名泛化会产生:
public static <T extends Comparable<T>> T max(Collection<T> coll)
That is, the method takes a collection of some type 也就是说,该方法获取与自身类似的某种类型T
that is comparable to itself, and returns an element of that type. T
的集合,并返回该类型的元素。However, this code turns out to be too restrictive. 然而,这段代码的限制性太强了。To see why, consider a type that is comparable to arbitrary objects:要了解为什么,可以考虑与任意对象相类似的类型:
class Foo implements Comparable<Object> { ... } Collection<Foo> cf = ... ; Collections.max(cf); // Should work.
Every element of cf
is comparable to every other element in cf
, since every such element is a Foo
, which is comparable to any object, and in particular to another Foo
. However, using the signature above, we find that the call is rejected. The inferred type must be Foo
, but Foo
does not implement Comparable<Foo>
.
It isn't necessary that 没有必要让T
be comparable to exactly itself. T
完全与它自己相比较。All that's required is that 所需要的只是T
be comparable to one of its supertypes. T
能够和它的一个超类型相比较。This give us:这给了我们:
public static <T extends Comparable<? super T>> T max(Collection<T> coll)
Note that the actual signature of Collections.max()
is more involved. We return to it in the next section, Converting Legacy Code to Use Generics. 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
).
It should be pretty clear by now that given:现在应该很清楚:
Set<?> unknownSet = new HashSet<String>(); ... /* Add an element t to a Set s. */ public static <T> void addToSet(Set<T> s, T t) { ... }
The call below is illegal.下面的调用是非法的。
addToSet(unknownSet, "abc"); // Illegal.
It makes no difference that the actual set being passed is a set of strings; what matters is that the expression being passed as an argument is a set of an unknown type, which cannot be guaranteed to be a set of strings, or of any type in particular.传递的实际集合是一组字符串,这一点没有区别;重要的是,作为参数传递的表达式是一个未知类型的集合,不能保证它是一组字符串,尤其是任何类型的字符串。
Now, consider the following code:现在,考虑下面的代码:
class Collections { ... <T> public static Set<T> unmodifiableSet(Set<T> set) { ... } } ... Set<?> s = Collections.unmodifiableSet(unknownSet); // This works! Why?
It seems this should not be allowed; yet, looking at this specific call, it is perfectly safe to permit it. 这似乎是不应该被允许的;然而,看看这个特定的电话,允许它是完全安全的。After all, 毕竟,unmodifiableSet()
does work for any kind of Set
, regardless of its element type.unmodifiableSet()
适用于任何类型的Set
,无论其元素类型如何。
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.此规则称为通配符捕获,允许编译器推断未知的通配符类型,作为泛型方法的类型参数。