有人可以向我解释为什么以下两个示例中的第一个可以编译,而第二个不可以吗?请注意,唯一的区别是第一个用 '.this' 明确限定了对 x 的引用,而第二个没有。在这两种情况下,最终字段 x 显然都在初始化之前尝试使用。
我原以为两个样本会被完全平等对待,导致两者都出现编译错误。
1)
public class Foo {
private final int x;
private Foo() {
int y = 2 * this.x;
x = 5;
}
}
2)
public class Foo {
private final int x;
private Foo() {
int y = 2 * x;
x = 5;
}
}
最佳答案
经过大量的规范阅读和思考,我得出结论:
在 Java 5 或 Java 6 编译器中,这是正确的行为。 Chapter 16 "Definite Assignment of The Java Language Specification, Third Edition说:
Each local variable (§14.4) and every blank
final
(§4.12.4) field (§8.3.1.2) must have a definitely assigned value when any access of its value occurs. An access to its value consists of the simple name of the variable occurring anywhere in an expression except as the left-hand operand of the simple assignment operator=
.
(强调我的)。所以在表达式 2 * this.x
中,this.x
部分 not 被认为是“访问 [x
's] value”(因此不受明确赋值规则的约束),因为 this.x
不是实例变量 x
的简单名称。 (注意,当发生明确分配时,在上面引用的文本之后的段落中,确实允许类似 this.x = 3
的内容,并考虑 x
之后明确分配;它只是不计入 this.x
的访问规则。)注意 this.x
在 this 中的值根据 §17.5.2,case 将为零.
在 Java 7 编译器中,这是一个编译器错误,但可以理解。 Chapter 16 "Definite Assignment" of the Java Language Specification, Java 7 SE Edition说:
Each local variable (§14.4) and every blank
final
field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by
this
) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator=
(§15.26.1).
(强调我的)。所以在表达式2 * this.x
中,this.x
部分应该被认为是“访问[x
's] value",并且应该给出一个编译错误。
但是你没有问是否第一个应该编译,你问为什么它确实编译(在某些编译器中)。这必然是推测性的,但我会做出两个猜测:
- 大多数 Java 7 编译器都是通过修改 Java 6 编译器编写的。一些编译器编写者可能没有注意到这种变化。此外,许多 Java-7 编译器和 IDE 仍然支持 Java 6,一些编译器编写者可能没有动力专门拒绝他们在 Java-6 模式中接受的 Java-7 模式中的某些内容。
- 新的 Java 7 行为奇怪地不一致。
(false ? null : this).x
之类的东西仍然是允许的,就此而言,甚至(this).x
仍然是允许的;只有特定的标记序列this
加上.
加上受此更改影响的字段名称。当然,这样的不一致已经存在于赋值语句的左侧(我们可以写this.x = 3
,但不能写成(this).x = 3
),但这更容易理解:它接受this.x = 3
作为禁止构造obj.x = 3
的特殊允许情况。允许这样做是有道理的。但我认为拒绝2 * this.x
作为其他允许的构造2 * obj.x
的特殊禁止情况是没有意义的,因为 (1 ) 这个特殊的禁止情况很容易通过添加括号来解决,(2) 这种特殊的禁止情况在以前的语言版本中是允许的,并且 (3) 我们仍然需要final
的特殊规则字段在初始化之前都有其默认值(例如0
用于int
),这都是因为(this).x
等情况,由于像this.foo()
这样的情况,其中foo()
是访问x
的方法。因此,一些编译器编写者可能没有动力做出这种不一致的更改。
其中任何一个都会令人惊讶——我假设编译器编写者对规范的每一次更改都有详细的信息,并且根据我的经验,Java 编译器通常非常擅长完全遵守规范(不像某些语言,每个编译器有自己的方言)——但是,发生了一些事情,以上只是我的两个猜测。
关于java - 使用未初始化的最终字段 - 带/不带 'this.' 限定符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13864464/