# 使用通配符更有趣

> 原文： [https://docs.oracle.com/javase/tutorial/extra/generics/morefun.html](https://docs.oracle.com/javase/tutorial/extra/generics/morefun.html)

在本节中，我们将考虑通配符的一些更高级的用法。我们已经看到了几个例子，当从数据结构中读取时，有界通配符很有用。现在考虑反向，一种只写数据结构。接口`Sink`就是这种简单的例子。

```
interface Sink<T> {
    flush(T t);
}

```

我们可以想象使用它，如下面的代码所示。方法`writeAll()`用于将集合`coll`的所有元素刷新到接收器`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.

```

如上所述，对`writeAll()`的调用是非法的，因为不能推断出有效的类型参数; `String`和`Object`都不是`T`的合适类型，因为`Collection`元素和`Sink`元素必须属于同一类型。

我们可以通过使用通配符修改`writeAll()`的签名来修复此错误，如下所示。

```
public static <T> T writeAll(Collection<? extends T>, Sink<T>) {...}
...
// Call is OK, but wrong return type. 
String str = writeAll(cs, s);

```

该调用现在是合法的，但是赋值是错误的，因为推断的返回类型是`Object`，因为`T`匹配`s`的元素类型，即`Object`。

解决方案是使用一种我们还没有看到的有界通配符形式：带有 _ 下界 _ 的通配符。语法`？ **super** T` 表示一种未知类型，它是`T`（或`T`本身的超类型;记住超类型关系是自反的）。它是我们一直在使用的有界通配符的双重性，我们使用`？ **扩展** T` 以表示作为`T`的亚型的未知类型。

```
public static <T> T writeAll(Collection<T> coll, Sink<? super T> snk) {
    ...
}
String str = writeAll(cs, s); // Yes! 

```

使用此语法，调用是合法的，并且根据需要推断类型为`String`。

现在让我们转向一个更现实的例子。 `java.util.TreeSet&lt;E&gt;`表示有序的`E`类型的元素树。构造`TreeSet`的一种方法是将`Comparator`对象传递给构造函数。该比较器将用于根据所需的顺序对`TreeSet`的元素进行排序。

```
TreeSet(Comparator<E> c) 

```

`Comparator`接口基本上是：

```
interface Comparator<T> {
    int compare(T fst, T snd);
}

```

假设我们想要创建一个`TreeSet&lt;String&gt;`并传入一个合适的比较器，我们需要传递一个可以比较`String`的`Comparator`。这可以通过`Comparator&lt;String&gt;`完成，但`Comparator&lt;Object&gt;`也可以。但是，我们将无法在`Comparator&lt;Object&gt;`上调用上面给出的构造函数。我们可以使用较低的有界通配符来获得我们想要的灵活性：

```
TreeSet(Comparator<? super E> c) 

```

该代码允许使用任何适用的比较器。

作为使用下限有界通配符的最后一个示例，让我们看一下方法`Collections.max()`，它返回作为参数传递给它的集合中的最大元素。现在，为了使`max()`起作用，传入的集合的所有元素必须实现`Comparable`。此外，它们必须都与 _ 彼此相当 _。

首次尝试生成此方法签名会产生：

```
public static <T extends Comparable<T>> T max(Collection<T> coll)

```

也就是说，该方法采用与自身相当的某种类型`T`的集合，并返回该类型的元素。但是，此代码过于严格。要了解原因，请考虑与任意对象相当的类型：

```
class Foo implements Comparable<Object> {
    ...
}
Collection<Foo> cf = ... ;
Collections.max(cf); // Should work.

```

`cf`的每个元素都与`cf`中的每个元素相当，因为每个这样的元素都是`Foo`，它可以与任何对象相比，特别是与另一个`Foo`相当。但是，使用上面的签名，我们发现该呼叫被拒绝。推断类型必须是`Foo`，但`Foo`不实现`Comparable&lt;Foo&gt;`。

`T`不必与**完全相当**本身。所需要的只是`T`与其超类型之一相当。这给了我们：

```
public static <T extends Comparable<? super T>> 
        T max(Collection<T> coll)

```

请注意，`Collections.max()`的实际签名更复杂。我们将在下一节回到它，[将遗留代码转换为使用泛型](convert.html)。这种推理几乎适用于任何类型的`Comparable`用法：你总是想使用 `Comparable&lt;？ **super** T&gt;` 。

通常，如果您的 API 只使用类型参数`T`作为参数，则其使用应该利用较低的有界通配符（`？ **super** T` ）。相反，如果 API 仅返回`T`，您将通过使用上限有通配符（`？**扩展** T` ）为您的客户提供更大的灵活性。

## 通配符捕获

现在应该很清楚，给出：

```
Set<?> unknownSet = new HashSet<String>();
...
/* Add an element  t to a Set s. */ 
public static <T> void addToSet(Set<T> s, T t) {
    ...
}

```

以下电话是非法的。

```
addToSet(unknownSet, "abc"); // Illegal.

```

传递的实际集合是一组字符串没有区别;重要的是，作为参数传递的表达式是一组未知类型，不能保证是一组字符串，特别是任何类型的字符串。

现在，请考虑以下代码：

```
class Collections {
    ...
    <T> public static Set<T> unmodifiableSet(Set<T> set) {
        ...
    }
}
...
Set<?> s = Collections.unmodifiableSet(unknownSet); // This works! Why?

```

看来这不应该被允许;但是，看看这个特定的电话，允许它是完全安全的。毕竟，`unmodifiableSet()`适用于任何类型的`Set`，无论其元素类型如何。

由于这种情况相对频繁地出现，因此有一个特殊的规则允许在非常特定的情况下使用这些代码，在这种情况下可以证明代码是安全的。此规则称为 _ 通配符捕获 _，允许编译器将通配符的未知类型推断为泛型方法的类型参数。