问题 Java 10 var和捕获变量


我在读 JEP 286 但我不明白这一部分:

使用嵌套捕获变量捕获变量和类型   投射到没有提到捕获变量的超类型。这个   映射将捕获变量替换为其上限和   替换带有有界的捕获变量的类型参数   通配符(然后重复)。这保留了传统上受限制的   捕获变量的范围,仅在单个范围内考虑   声明。

谁能让我在Java代码中具体说明它的含义?


4934
2017-08-01 10:19


起源



答案:


var 允许您推断不可表示的类型:

var x = new Object() {
    int i = 10;
};

System.out.println(x.i); // works; `x` has the non-denotable type of the annonymous class

理论上,这将允许您推断通配符类型。但是这篇文章所说的是,这是不可能的,因为通配符被它的上限或者推断类型中的新捕获变量所取代。

以这段代码为例:

List<String> l1 = new ArrayList<>();
l1.add("Hello");
List<?> l2 = l1;

var x = l2.get(0);
l2.add(x); // error

在这里,而不是类型 x 被推断为通配符的确切类型,这将使​​最后一行编译。相反,它被推断为它的上限,即 Object,并得到(Eclipse)错误消息:

The method add(capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (Object)

你可以在哪里看到的类型 x 是 Object

那是一部分

此映射将捕获变量替换为其上限


第二部分

...并使用有界通配符替换提及捕获变量的类型参数

谈论这样的情况:

List<String> l1 = new ArrayList<>();
l1.add("Hello");
List<?> l2 = l1;
var l3 = l2; // type of 'l3' is List<?>, but not the same '?' as 'l2'

l3.add(l2.get(0)); // error

这也不编译,因为类型 l3 与类型不完全相同 l2,表示从中返回的类型 l2.get(0) 与所要求的类型不同 l3.add(...)。这里的错误是:

The method add(capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (capture#3-of ?)

而且你看到两个捕获变量都是不同的,这意味着它的类型 l3 不完全是那种类型 l2,但是类型的捕获变量 l2 在推断类型中,用具有相同边界的通配符替换,然后为其创建新的捕获变量。

所以对于一个类型 List<capture#1-of ?> 推断类型是 List<?>,然后编译器为该通配符创建一个新的捕获变量,让步 List<capture#2-of ?> (虽然编号在实践中可能有不同的作用,关键是2个捕获变量是不同的)。


15
2017-08-01 11:25



很好的解释!对于这个“和嵌套捕获变量的类型”?一些例子? - xdevel2000
@ xdevel2000 List<?> 是具有嵌套捕获变量的类型。因此对于 List<capture#1-of ?> 正在投射的超级类型是 List<?> (使用通配符),然后编译器会为其创建一个新的捕获变量。 - Jorn Vernee
我认为这样的例子 var x = l2.get(0); l2.add(x); 即使没有这个规则,仍然无法编译,因为JLS要求每次捕获转换时都会创建一个“新的”捕获变量(看到)。这些错误看起来就像 The method add(capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (capture#1-of ?),就像你尝试做类似的事情一样 l2.add(l2.get(0))。 - Radiodef
@Radiodef规范似乎谈到了转换为可表示类型的情况: “从参数化类型G <T1,...,Tn>(§4.5)到参数化类型G <S1,...,Sn>” 背后的想法 var 是它推断出指定值的类型,所以从技术上讲,我认为没有转换。但它在任何方面都令人困惑,因此JEP文本可以解决这个问题。 - Jorn Vernee
通配符不是正确的类型(它们没有成员,子类型,超类型等),因此捕获转换会在它们用作类型时将它们转换为类型变量。在一个案例中 l2.add(x),搜索类型的成员时发生捕获转换 l2 (一个成员 List<?> 是捕获转换后的成员并且在类似的情况下 l2.get(), 捕获转换特别适用于返回类型。 - Radiodef


答案:


var 允许您推断不可表示的类型:

var x = new Object() {
    int i = 10;
};

System.out.println(x.i); // works; `x` has the non-denotable type of the annonymous class

理论上,这将允许您推断通配符类型。但是这篇文章所说的是,这是不可能的,因为通配符被它的上限或者推断类型中的新捕获变量所取代。

以这段代码为例:

List<String> l1 = new ArrayList<>();
l1.add("Hello");
List<?> l2 = l1;

var x = l2.get(0);
l2.add(x); // error

在这里,而不是类型 x 被推断为通配符的确切类型,这将使​​最后一行编译。相反,它被推断为它的上限,即 Object,并得到(Eclipse)错误消息:

The method add(capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (Object)

你可以在哪里看到的类型 x 是 Object

那是一部分

此映射将捕获变量替换为其上限


第二部分

...并使用有界通配符替换提及捕获变量的类型参数

谈论这样的情况:

List<String> l1 = new ArrayList<>();
l1.add("Hello");
List<?> l2 = l1;
var l3 = l2; // type of 'l3' is List<?>, but not the same '?' as 'l2'

l3.add(l2.get(0)); // error

这也不编译,因为类型 l3 与类型不完全相同 l2,表示从中返回的类型 l2.get(0) 与所要求的类型不同 l3.add(...)。这里的错误是:

The method add(capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (capture#3-of ?)

而且你看到两个捕获变量都是不同的,这意味着它的类型 l3 不完全是那种类型 l2,但是类型的捕获变量 l2 在推断类型中,用具有相同边界的通配符替换,然后为其创建新的捕获变量。

所以对于一个类型 List<capture#1-of ?> 推断类型是 List<?>,然后编译器为该通配符创建一个新的捕获变量,让步 List<capture#2-of ?> (虽然编号在实践中可能有不同的作用,关键是2个捕获变量是不同的)。


15
2017-08-01 11:25



很好的解释!对于这个“和嵌套捕获变量的类型”?一些例子? - xdevel2000
@ xdevel2000 List<?> 是具有嵌套捕获变量的类型。因此对于 List<capture#1-of ?> 正在投射的超级类型是 List<?> (使用通配符),然后编译器会为其创建一个新的捕获变量。 - Jorn Vernee
我认为这样的例子 var x = l2.get(0); l2.add(x); 即使没有这个规则,仍然无法编译,因为JLS要求每次捕获转换时都会创建一个“新的”捕获变量(看到)。这些错误看起来就像 The method add(capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (capture#1-of ?),就像你尝试做类似的事情一样 l2.add(l2.get(0))。 - Radiodef
@Radiodef规范似乎谈到了转换为可表示类型的情况: “从参数化类型G <T1,...,Tn>(§4.5)到参数化类型G <S1,...,Sn>” 背后的想法 var 是它推断出指定值的类型,所以从技术上讲,我认为没有转换。但它在任何方面都令人困惑,因此JEP文本可以解决这个问题。 - Jorn Vernee
通配符不是正确的类型(它们没有成员,子类型,超类型等),因此捕获转换会在它们用作类型时将它们转换为类型变量。在一个案例中 l2.add(x),搜索类型的成员时发生捕获转换 l2 (一个成员 List<?> 是捕获转换后的成员并且在类似的情况下 l2.get(), 捕获转换特别适用于返回类型。 - Radiodef