有人可以向我解释为什么以下两个样本中的第一个编译,而第二个没有?注意唯一的区别是第一个明确地使用'.this'限定对x的引用,而第二个没有.在这两种情况下,显然都会尝试在初始化之前使用最终字段x.
我原以为两个样本都会被完全平等对待,导致两者都有编译错误.
1)
public class Foo {
private final int x;
private Foo() {
int y = 2 * this.x;
x = 5;
}
}
Run Code Online (Sandbox Code Playgroud)
2)
public class Foo {
private final int x;
private Foo() {
int y = 2 * x;
x = 5;
}
}
Run Code Online (Sandbox Code Playgroud)
rua*_*akh 39
经过一系列的规范阅读和思考后,我得出结论:
在Java 5或Java 6编译器中,这是正确的行为.第16章" Java语言规范的明确赋值,第三版说:
当对其值进行任何访问时,每个局部变量(第14.4节)和每个空白
final(第4.12.4节)字段(第8.3.1.2节)必须具有明确赋值.对其值的访问包括在表达式中任何位置出现的变量的简单名称,除了作为简单赋值运算符的左侧操作数=.
(强调我的).因此在表达式中2 * this.x,该this.x部分不被视为"[ xs]值的访问"(因此不受明确赋值的规则约束),因为this.x它不是实例变量的简单名称x.(注意当明确赋值发生时,在上面引用的文本之后的段落中的规则确实允许类似的东西this.x = 3,并且考虑x之后明确分配;它只是不计数的访问规则this.x.)注意值this.x根据§17.5.2,在这种情况下将为零.
在Java 7编译器中,这是一个编译器错误,但是可以理解.Java语言规范的第16章"定义分配" ,Java 7 SE Edition说:
当对其值进行任何访问时,每个局部变量(第14.4节)和每个空白
final字段(第4.12.4节,第8.3.1.2节)必须具有明确赋值.对其值的访问包括变量的简单名称(或者,对于字段,限定字段的简单名称
this),除了作为简单赋值运算符的左手操作数=(第15.26节)外,它出现在表达式的任何位置. 1).
(强调我的).因此在表达式中2 * this.x,该this.x部分应被视为"访问[ x's]值",并且应该给出编译错误.
但你没有问是否第一个应该编译,你问为什么它不编译(在一些编译器).这必然是推测性的,但我会做两个猜测:
(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编译器通常非常适合完全遵守规范(不像某些语言,每个编译器都有它的自己的方言) - 但是,事情发生了,以上是我唯一的两个猜测.
| 归档时间: |
|
| 查看次数: |
2346 次 |
| 最近记录: |