通配符catch和帮助器方法

在某些情况下,编译器会推断通配符的类型。例如,列表可以定义为List\<?\>,但是在评估表达式时,编译器会从代码中推断出特定类型。这种情况称为通配符catch

在大多数情况下,您无需担心通配符catch,除非您看到包含短语“ capture of”的错误消息。

WildcardError示例在编译时产生catch错误:

import java.util.List;

public class WildcardError {

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

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

由 Oracle 的 JDK 7 javac实现编译时,WildcardError示例将产生以下错误:

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

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

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));
    }

}

由于使用了 helper 方法,编译器在调用中使用推断来确定TCAP\#1,即catch变量。该示例现在可以成功编译。

按照约定,辅助方法通常命名为*originalMethodName* Helper

现在考虑一个更复杂的示例WildcardErrorBad

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
    }
}

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

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

虽然List\<Integer\>List\<Double\>都满足List\<? extends Number\>的条件,但显然要从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

没有解决该问题的辅助方法,因为代码从根本上是错误的。