在某些情况下,编译器会推断通配符的类型。例如,可以将列表定义为List <?>,但是在计算表达式时,编译器会从代码中推断出特定类型。这种情况称为通配符捕获(wildcard capture
    在大多数情况下,您无需担心通配符捕获,除非您看到包含短语“ capture of”的错误消息。
    WildcardError示例在编译时产生捕获错误:

    1. import java.util.List;
    2. public class WildcardError {
    3. void foo(List<?> i) {
    4. i.set(0, i.get(0));
    5. }
    6. }

    在此示例中,编译器将输入参数i处理为Object类型。当foo方法调用 List.set(int,E)时,编译器无法确认要插入列表中的对象的类型,并产生错误。当发生这种类型的错误时,通常意味着编译器认为您正在将错误的类型分配给变量。为此,将泛型添加到Java语言中-以便在编译时强制类型安全。
    由Oracle的JDK 7 javac实现编译时 ,WildcardError示例将生成以下错误:

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

    在此示例中,代码正在尝试执行安全操作,那么如何解决编译器错误?您可以通过编写捕获通配符的私有帮助器方法(private helper method来修复它。在这种情况下,您可以通过创建私有帮助器方法fooHelper来解决此问题,如下WildcardFixed所示:

    1. public class WildcardFixed {
    2. void foo(List<?> i) {
    3. fooHelper(i);
    4. }
    5. // Helper method created so that the wildcard can be captured
    6. // through type inference.
    7. private <T> void fooHelper(List<T> l) {
    8. l.set(0, l.get(0));
    9. }
    10. }

    借助helper方法,编译器在调用中使用推断来确定T是CAP#1(捕获变量)。该示例现在可以成功编译。
    按照约定,帮助器方法通常被命名为_originalMethodName_Helper。
    现在考虑一个更复杂的示例 WildcardErrorBad

    1. import java.util.List;
    2. public class WildcardErrorBad {
    3. void swapFirst(List<? extends Number> l1, List<? extends Number> l2) {
    4. Number temp = l1.get(0);
    5. l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
    6. // got a CAP#2 extends Number;
    7. // same bound, but different types
    8. l2.set(0, temp); // expected a CAP#1 extends Number,
    9. // got a Number
    10. }
    11. }

    此示例代码正在尝试不安全的操作。例如,考虑对swapFirst方法的以下调用:

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

    虽然List 和List 都满足List<? extends Number>的条件,但从Integer列表中取出一项并将其放入Double值列表中显然是不正确的。
    使用Oracle的JDK javac编译器编译代码会产生以下错误:

    1. WildcardErrorBad.java:7: error: method set in interface List<E> cannot be applied to given types;
    2. l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
    3. ^
    4. required: int,CAP#1
    5. found: int,Number
    6. reason: actual argument Number cannot be converted to CAP#1 by method invocation conversion
    7. where E is a type-variable:
    8. E extends Object declared in interface List
    9. where CAP#1 is a fresh type-variable:
    10. CAP#1 extends Number from capture of ? extends Number
    11. WildcardErrorBad.java:10: error: method set in interface List<E> cannot be applied to given types;
    12. l2.set(0, temp); // expected a CAP#1 extends Number,
    13. ^
    14. required: int,CAP#1
    15. found: int,Number
    16. reason: actual argument Number cannot be converted to CAP#1 by method invocation conversion
    17. where E is a type-variable:
    18. E extends Object declared in interface List
    19. where CAP#1 is a fresh type-variable:
    20. CAP#1 extends Number from capture of ? extends Number
    21. WildcardErrorBad.java:15: error: method set in interface List<E> cannot be applied to given types;
    22. i.set(0, i.get(0));
    23. ^
    24. required: int,CAP#1
    25. found: int,Object
    26. reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion
    27. where E is a type-variable:
    28. E extends Object declared in interface List
    29. where CAP#1 is a fresh type-variable:
    30. CAP#1 extends Object from capture of ?
    31. 3 errors

    没有解决此问题的帮助器方法,因为代码根本上是错误的。