Buh*_*ndi 20
捕获转换旨在使通配符(在泛型中)?很有用.
假设我们有以下课程:
public interface Test<T> {
public void shout(T whatever);
public T repeatPreviousShout();
}
Run Code Online (Sandbox Code Playgroud)
我们的代码在某处,
public static void instantTest(Test<?> test) {
System.out.println(test.repeatPreviousShout());
}
Run Code Online (Sandbox Code Playgroud)
因为test不是原始的Test,因为repeatPreviousShout()在"后见之明"中返回a ?,编译器知道有一个T用作类型参数Test.这T是针对某些未知的,T因此编译器会删除未知类型(对于通配符,它将替换为Object.),因此repeatPreviousShout()返回一个Object.
但如果我们有,
public static void instantTest2(Test<?> test) {
test.shout(test.repeatPreviousShout());
}
Run Code Online (Sandbox Code Playgroud)
编译器会给我们一个类似的错误Test<capture#xxx of ?> cannot be applied(xxx例如,数字在哪里337).
这是因为编译器尝试进行类型安全检查shout()但由于它收到了通配符,因此它不知道T代表什么,因此它创建了一个名为capture的占位符.
从这里(Java理论和实践:与泛型一起疯狂,第1部分),它明确指出:
捕获转换允许编译器为捕获的通配符制作占位符类型名称,因此类型推断可以将其推断为该类型.
希望这对你有所帮助.
涉及通配符类型参数的参数化类型实际上是联合类型。例如
List<? extends Number> = Union{ List<S> | S <: Number }
Run Code Online (Sandbox Code Playgroud)
在两种情况下,Java 不使用List<? extends Number>,而是使用捕获的版本List<S>,其中 S 是刚刚创建的具有上限的类型变量Number。
(1)http://java.sun.com/docs/books/jls/third_edition/html/expressions.html
缩小表达式的类型。如果一个表达式的类型是List<? extends Number>,我们可以确定该对象的运行时类型实际上是List<S>某个具体类型 S ( S <: Number>) 的 a 。因此编译器使用List<S>,来执行更准确的类型分析。
捕获转换单独应用于每个表达式;这会导致一些愚蠢的结果:
<T> void test1(List<T> a){}
<T> void test2(List<T> a, List<T> b){}
List<?> x = ...;
test1(x); // ok
test2(x, x); // error
Run Code Online (Sandbox Code Playgroud)
(2)http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.2
在子类型检查中A :< B,其中A涉及通配符参数。例如,
List<? extends Number> :< B
<=>
Union{ List<S> | S <: Number} :< B
<=>
List<S> :< B, for all S <: Number
Run Code Online (Sandbox Code Playgroud)
所以实际上,我们正在检查类型的捕获版本A