# 通配符捕获和帮助方法

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

在某些情况下，编译器会推断出通配符的类型。例如，列表可以被定义为 `List&lt;？&gt;。` 但是，在评估表达式时，编译器会从代码中推断出特定的类型。这种情况称为 _ 通配符捕获 _。

在大多数情况下，您不必担心通配符捕获，除非您看到包含短语“capture of”的错误消息。

[`WildcardError`](examples/WildcardError.java) 示例在编译时产生捕获错误：

```
import java.util.List;

public class WildcardError {

    void foo(List<?> i) {
        i.set(0, i.get(0));
    }
}

```

在此示例中，编译器将 `i` 输入参数处理为 `Object` 类型。当 `foo` 方法调用 [List.set（int，E）](https://docs.oracle.com/javase/8/docs/api/java/util/List.html#set-int-E-)时，编译器无法确认插入到列表中的对象类型，并产生错误。发生此类错误时，通常意味着编译器认为您为变量分配了错误的类型。由于这个原因，泛型被添加到 Java 语言中 - 在编译时强制类型安全。

`WildcardError` 示例在由 Oracle 的 JDK 7 `javac` 实现编译时生成以下错误：

```
WildcardError.java:6: error: method set in interface List<E> cannot be applied to given types;
    i.set(0, i.get(0));
     ^
  required: int,CAP#1
  found: int,Object
  reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error

```

在此示例中，代码尝试执行安全操作，那么如何解决编译器错误？您可以通过编写捕获通配符的 _ 私有帮助程序方法 _ 来修复它。在这种情况下，您可以通过创建私有帮助方法 `fooHelper` 来解决问题，如 [`WildcardFixed`](examples/WildcardFixed.java) 所示：

```
public class WildcardFixed {

    void foo(List<?> i) {
        fooHelper(i);
    }

    // Helper method created so that the wildcard can be captured
    // through type inference.
    private <T> void fooHelper(List<T> l) {
        l.set(0, l.get(0));
    }

}

```

由于辅助方法，编译器使用推断来确定 `T` 是 `CAP＃1` ，即调用中的捕获变量。该示例现在已成功编译。

按照惯例，辅助方法通常被命名为 `_originalMethodName_ Helper` 。

现在考虑一个更复杂的例子， [`WildcardErrorBad`](examples/WildcardErrorBad.java) ：

```
import java.util.List;

public class WildcardErrorBad {

    void swapFirst(List<? extends Number> l1, List<? extends Number> l2) {
      Number temp = l1.get(0);
      l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
                            // got a CAP#2 extends Number;
                            // same bound, but different types
      l2.set(0, temp);	    // expected a CAP#1 extends Number,
                            // got a Number
    }
}

```

在此示例中，代码尝试进行不安全的操作。例如，考虑以下 `swapFirst` 方法的调用：

```
List<Integer> li = Arrays.asList(1, 2, 3);
List<Double>  ld = Arrays.asList(10.10, 20.20, 30.30);
swapFirst(li, ld);

```

而 `List&lt; Integer&gt;` 和`列表&lt; Double&gt;` 均符合 `List&lt;？的标准 extends Number&gt;` ，从 `Integer` 值列表中取一个项目并尝试将其放入 `Double` 值列表中显然是不正确的。

使用 Oracle 的 JDK `javac` 编译器编译代码会产生以下错误：

```
WildcardErrorBad.java:7: error: method set in interface List<E> cannot be applied to given types;
      l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
        ^
  required: int,CAP#1
  found: int,Number
  reason: actual argument Number cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Number from capture of ? extends Number
WildcardErrorBad.java:10: error: method set in interface List<E> cannot be applied to given types;
      l2.set(0, temp);      // expected a CAP#1 extends Number,
        ^
  required: int,CAP#1
  found: int,Number
  reason: actual argument Number cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Number from capture of ? extends Number
WildcardErrorBad.java:15: error: method set in interface List<E> cannot be applied to given types;
        i.set(0, i.get(0));
         ^
  required: int,CAP#1
  found: int,Object
  reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
3 errors

```

没有帮助方法来解决这个问题，因为代码根本就是错误的。