有人可以向我解释为什么以下两个样本中的第一个编译,而第二个没有?注意唯一的区别是第一个明确地使用'.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;
}
}
答案 0 :(得分:39)
经过一系列的规范阅读和思考后,我得出结论:
在Java 5或Java 6编译器中,这是正确的行为。 Chapter 16 "Definite Assignment of The Java Language Specification, Third Edition说:
每个本地变量(§14.4)和每个空白
final
(§4.12.4)字段(§8.3.1.2)在对其值进行任何访问时都必须具有明确分配的值。对值的访问包括变量的简单名称出现在表达式中的任何位置,除了作为简单赋值运算符=
的左侧操作数。
(强调我的)。因此,在2 * this.x
表达式中,this.x
部分不被视为“[x
的值的访问权限”(因此不受明确赋值的规则),因为this.x
不是实例变量x
的简单名称。 (注意当发生明确赋值时的规则,在上述引用文本之后的段落中, 允许this.x = 3
之类的内容,并认为此后明确分配x
;它只是不计入this.x
的访问规则。)请注意,在这种情况下,this.x
的值将为零,每§17.5.2。
在Java 7编译器中,这是一个编译器错误,但是可以理解。 Chapter 16 "Definite Assignment" of the Java Language Specification, Java 7 SE Edition说:
每个局部变量(§14.4)和每个空白
final
字段(§4.12.4,§8.3.1.2)必须具有明确分配的值访问其价值。对其值的访问权限包含变量的简单名称(或者,对于字段,由
this
限定的字段的简单名称)出现在表达式中的任何位置除外作为简单赋值运算符=
(§15.26.1)的左手操作数。
(强调我的)。所以在表达式2 * this.x
中,this.x
部分应被视为“访问[x
的'值',而应该给出编译错误。
但是你没有问 (在一些编译器中)。这必然是推测性的,但我会做两个猜测:
(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
表示{{1} }})直到它们被初始化,因为像int
这样的情况,并且因为像(this).x
这样的情况this.foo()
是访问foo()
的方法。因此,一些编译器编写者可能没有动力去做出这种不一致的改变。这些都是令人惊讶的 - 我假设编译器编写者有关于规范的每一次更改的详细信息,根据我的经验,Java编译器通常非常适合完全遵守规范(不像某些语言,每个编译器有自己的方言) - 但是,好吧,发生了某事,以上是我唯一的两个猜测。
答案 1 :(得分:2)
在构造函数中使用this
时,编译器将 x
视为this
对象的成员属性(默认初始化)。由于x
为int
,因此默认使用0
进行初始化。这使得编译器很高兴,并且它在运行时也能正常工作。
当你不使用this
时,编译器在词法分析中直接使用x
声明,因此它抱怨它的初始化(编译时现象)。
所以它是this
的定义,它使得编译器在编译中的词法分析期间将x
分析为对象的成员变量与直接属性,并导致不同的编译行为。
当用作主表达式时,关键字this表示一个值,该值是对调用实例方法的对象(第15.12节)或对正在构造的对象的引用。
答案 2 :(得分:-1)
我认为编译器估计编写this.x意味着'this'存在,因此调用了构造函数(并且已经初始化了最终变量)。 但是在尝试运行它时应该得到RuntimeException
答案 3 :(得分:-1)
我假设你引用了Eclipse中的行为。 (正如评论javac的编译一样)。
我认为这是一个Eclipse问题。它有自己的编译器和自己的规则集。其中之一是您可能无法访问未初始化的字段,尽管Java-commpiler会为您初始化变量。