经过大量的规范阅读和思考后,我得出的结论是:
在 Java 5 或 Java 6 编译器中,这是正确的行为。第16章“明确指派Java 语言规范, 第三版 http://docs.oracle.com/javase/specs/jls/se5.0/html/defAssign.html says:
每个局部变量(§14.4) http://docs.oracle.com/javase/specs/jls/se5.0/html/statements.html#5920和每一个空白final
(§4.12.4) http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#10931 field (§8.3.1.2) http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#35962必须有一个明确指定当对其值进行任何访问时的值。对其值的访问包括变量的简单名称出现在表达式中的任何位置,但作为简单赋值运算符的左侧操作数除外=
.
(强调我的)。所以在表达式中2 * this.x
, the this.x
部分是not被认为是“访问[x
的]值”(因此不受明确赋值的规则的约束),因为this.x
不是实例变量的简单名称x
。 (注意,在上述引用文本之后的段落中,发生明确赋值时的规则,does允许类似的事情this.x = 3
,并考虑x
此后将被明确指定;这只是不计算在内的访问规则this.x
.) 请注意this.x
在这种情况下将为零,每§17.5.2 http://docs.oracle.com/javase/specs/jls/se5.0/html/memory.html#17.5.2.
在 Java 7 编译器中,这是一个编译器错误,但可以理解。第16章“明确指派”Java语言规范, Java 7 SE 版 http://docs.oracle.com/javase/specs/jls/se7/html/jls-16.html says:
每个局部变量(§14.4 http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.4) 和每个空白final
field (§4.12.4 http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4, §8.3.1.2 http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2)必须有一个明确指定当对其值进行任何访问时的值。
对其值的访问由变量的简单名称组成(或者,对于字段,由以下条件限定的字段的简单名称this
)出现在表达式中的任何位置,但作为简单赋值运算符的左侧操作数除外=
(§15.26.1 http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26.1).
(强调我的)。所以在表达式中2 * this.x
, the this.x
part should被视为“访问[x
的]值”,以及should给出编译错误。
但你没有问whether第一个should编译,你问why it does编译(在某些编译器中)。这必然是推测,但我会做出两个猜测:
- 大多数 Java 7 编译器是通过修改 Java 6 编译器编写的。一些编译器编写者可能没有注意到这一变化。此外,许多 Java-7 编译器和 IDE 仍然支持 Java 6,并且一些编译器编写者可能没有动力专门拒绝 Java-7 模式中他们在 Java-6 模式中接受的某些内容。
- 新的 Java 7 行为奇怪地不一致。就像是
(false ? null : this).x
仍然是允许的,就此而言,甚至(this).x
仍然允许;这只是特定的令牌序列this
plus .
加上受此更改影响的字段名称。诚然,这样的不一致已经存在于赋值语句的左侧(我们可以写成this.x = 3
, 但不是(this).x = 3
),但这更容易理解:它接受this.x = 3
作为其他禁止施工的特殊允许情况obj.x = 3
。允许这样做是有道理的。但我认为拒绝没有意义2 * this.x
作为其他允许的建筑的特殊禁止情况2 * obj.x
,鉴于 (1) 这种特殊禁止的情况很容易通过添加括号来解决,(2) 这种特殊禁止的情况在该语言的早期版本中是允许的,并且 (3) 我们仍然需要特殊的规则final
字段有其默认值(例如0
for an int
)直到它们被初始化,这都是因为像这样的情况(this).x
,并且由于像这样的情况this.foo()
where foo()
是一个访问方法x
。因此,一些编译器编写者可能没有动力做出这种不一致的更改。
其中任何一个都会令人惊讶——我认为编译器编写者有关于规范的每一个更改的详细信息,并且根据我的经验,Java 编译器通常非常善于准确地遵守规范(与某些语言不同,每个编译器都有自己的规范)自己的方言)——但是,好吧,某物发生了,以上是我仅有的两个猜测。