The Java Tutorials have been written for JDK 8.Java教程是为JDK 8编写的。Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available.本页中描述的示例和实践没有利用后续版本中引入的改进,并且可能使用不再可用的技术。See Java Language Changes for a summary of updated language features in Java SE 9 and subsequent releases.有关Java SE 9及其后续版本中更新的语言特性的摘要,请参阅Java语言更改。
See JDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases.有关所有JDK版本的新功能、增强功能以及已删除或不推荐的选项的信息,请参阅JDK发行说明。
In some cases, the compiler infers the type of a wildcard. 在某些情况下,编译器会推断通配符的类型。For example, a list may be defined as List<?> but, when evaluating an expression, the compiler infers a particular type from the code. 例如,列表可以定义为List<?>,但是,在计算表达式时,编译器会从代码中推断出特定的类型。This scenario is known as wildcard capture.这种情况称为通配符捕获。
For the most part, you don't need to worry about wildcard capture, except when you see an error message that contains the phrase "capture of".在大多数情况下,您不需要担心通配符捕获,除非您看到包含短语“捕获”的错误消息。
The WildcardError
example produces a capture error when compiled:WildcardError
示例在编译时生成捕获错误:
import java.util.List; public class WildcardError { void foo(List<?> i) { i.set(0, i.get(0)); } }
In this example, the compiler processes the i input parameter as being of type Object. 在本例中,编译器将i输入参数处理为Object类型。When the foo method invokes List.set(int, E), the compiler is not able to confirm the type of object that is being inserted into the list, and an error is produced. 当foo方法调用List.set(int, E),时,编译器无法确认要插入列表的对象的类型,并产生错误。When this type of error occurs it typically means that the compiler believes that you are assigning the wrong type to a variable. 当发生这种类型的错误时,通常意味着编译器认为您为变量指定了错误的类型。Generics were added to the Java language for this reason — to enforce type safety at compile time.为此,Java语言中添加了泛型—在编译时强制执行类型安全。
The WildcardError example generates the following error when compiled by Oracle's JDK 7 javac implementation: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
In this example, the code is attempting to perform a safe operation, so how can you work around the compiler error? 在本例中,代码试图执行安全操作,因此如何解决编译器错误?You can fix it by writing a private helper method which captures the wildcard. 您可以通过编写捕获通配符的私有辅助方法来修复它。In this case, you can work around the problem by creating the private helper method, fooHelper, as shown in 在这种情况下,您可以通过创建私有辅助方法fooHelper来解决此问题,如WildcardFixed
: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)); } }
Thanks to the helper method, the compiler uses inference to determine that T is CAP#1, the capture variable, in the invocation. 多亏了辅助方法,编译器在调用中使用推理来确定T是CAP#1,即捕获变量。The example now compiles successfully.该示例现在已成功编译。
By convention, helper methods are generally named originalMethodNameHelper.按照惯例,辅助方法通常被命名为originalMethodNameHelper。
Now consider a more complex example, 现在考虑一个更复杂的例子,WildcardErrorBad
: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 } }
In this example, the code is attempting an unsafe operation. 在本例中,代码正在尝试不安全的操作。For example, consider the following invocation of the swapFirst method:例如,考虑以下swapFirst方法的调用:
List<Integer> li = Arrays.asList(1, 2, 3); List<Double> ld = Arrays.asList(10.10, 20.20, 30.30); swapFirst(li, ld);
While List<Integer> and List<Double> both fulfill the criteria of List<? extends Number>, it is clearly incorrect to take an item from a list of Integer values and attempt to place it into a list of Double values.虽然List<Integer>和List<Double>都满足了List<? extends Number>的条件,但是它明显错误取用了来自Integer值列表的项,试图把它放到Double值的列表中。
Compiling the code with Oracle's JDK javac compiler produces the following error:使用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
There is no helper method to work around the problem, because the code is fundamentally wrong: it is clearly incorrect to take an item from a list of Integer values and attempt to place it into a list of Double values.没有辅助方法可以解决这个问题,因为代码从根本上是错误的:从Integer值列表中获取一个项并尝试将其放入Double值列表中显然是错误的。