22

我注意到 JLS 谈到5.1.10 Capture Conversion,但我不明白它们是什么。

任何人都可以向我解释/举例吗?

4

2 回答 2

27

捕获转换旨在使通配符(在泛型中)?有用。

假设我们有以下类:

public interface Test<T> {
    public void shout(T whatever);
    public T repeatPreviousShout();

}

在我们的代码的某个地方,

public static void instantTest(Test<?> test) {
    System.out.println(test.repeatPreviousShout());
}

因为test不是原始的Test,并且因为repeatPreviousShout()在“后见之明”中返回 a ?,所以编译器知道有 aT用作 的类型参数Test。这T是针对某些未知T的,因此编译器会删除未知类型(对于通配符,它​​会替换为Object)。因此repeatPreviousShout()返回一个Object.

但如果我们有,

public static void instantTest2(Test<?> test) {
    test.shout(test.repeatPreviousShout());
}

编译器会给我们一个类似的错误Test<capture#xxx of ?> cannot be applied(其中xxx是一个数字,例如337)。

这是因为编译器尝试进行类型安全检查,shout()但由于它收到一个通配符,它​​不知道T代表什么,因此它创建了一个名为capture of的占位符。

这里(Java 理论与实践:Going with generics, Part 1),它清楚地指出:

捕获转换允许编译器为捕获的通配符制造占位符类型名称,以便类型推断可以推断出它是该类型。

希望这对您有所帮助。

于 2010-12-13T18:13:06.673 回答
4

涉及通配符类型参数的参数化类型实际上是联合类型。例如

List<? extends Number> = Union{ List<S> | S <: Number }

在 2 种情况下List<? extends Number>,Java 不使用 ,而是使用捕获的版本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

(2) http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.2

在子类型检查A :< BA涉及通配符参数的地方。例如,

List<? extends Number>  :< B
<=>
Union{ List<S> | S <: Number}  :< B
<=>
List<S> :< B, for all S <: Number

所以实际上,我们正在检查捕获的类型版本A

于 2011-09-05T23:46:57.253 回答