通配符捕获和帮助方法

原文: https://docs.oracle.com/javase/tutorial/java/generics/capture.html

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

在大多数情况下,您不必担心通配符捕获,除非您看到包含短语“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 语言中 - 在编译时强制类型安全。

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

  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

在此示例中,代码尝试执行安全操作,那么如何解决编译器错误?您可以通过编写捕获通配符的私有帮助程序方法来修复它。在这种情况下,您可以通过创建私有帮助方法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. }

由于辅助方法,编译器使用推断来确定TCAP#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&lt; Integer>列表&lt; Double>均符合List&lt;?的标准 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

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