在某些状况下,编译器会推断出通配符的类型,例如,列表能够定义为List<?>
,可是在评估表达式时,编译器会从代码中推断出特定类型,此场景称为通配符捕获。html
在大多数状况下,你没必要担忧通配符捕获,除非你看到包含短语“capture of”的错误消息。java
WildcardError
示例在编译时产生捕获错误:segmentfault
import java.util.List; public class WildcardError { void foo(List<?> i) { i.set(0, i.get(0)); } }
在此示例中,编译器将i
输入参数处理为Object
类型,当foo
方法调用List.set(int, E)时,编译器没法确认插入到列表中的对象的类型,而且会产生错误,发生此类错误时,一般意味着编译器认为你为变量分配了错误的类型,出于这个缘由,泛型被添加到Java语言中 — 在编译时强制执行类型安全。api
由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
在此示例中,代码尝试执行安全操做,那么如何解决编译器错误?你能够经过编写捕获通配符的私有Helper方法来修复它,在这种状况下,你能够经过建立私有Helper方法fooHelper
来解决此问题,如WildcardFixed
中所示:oracle
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方法,编译器使用推断来肯定T
是调用中的CAP#1
(捕获变量),该示例如今成功编译。app
按照惯例,Helper方法一般命名为originalMethodNameHelper
。ui
如今考虑一个更复杂的例子,WildcardErrorBad
:code
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
方法的调用:htm
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
这里没有Helper方法来解决这个问题,由于代码根本就是错误的。