使用未初始化的最终字段 – 带/不带“this”。 预选赛

有人可以向我解释为什么以下两个样本中的第一个编译,而第二个不是? 注意唯一的区别是第一个显式限定了对'.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编译器中,这是正确的行为。 第16章“ Java语言规范的明确分配”第三版说:

每个局部variables(§14.4)和每个空白的final (§4.12.4)域(§8.3.1.2)都必须有一个明确赋值的值, 对其值的访问由简单赋值运算符=的左操作数以外的expression式中简单的名称组成。

(强调我的)。 因此,在expression式2 * this.xthis.x部分被视为“访问[ x 's]值”(因此不受this.x明确赋值的规则),因为this.x不是实例variablesx简单名称。 (注意在明确赋值发生时的规则,在上面引用的文本之后的段落中, 确实允许类似this.x = 3 ,并认为之后明确赋值x ;这只是访问规则不计数)请注意,在这种情况下, this.x的值将根据§17.5.2为零。

在Java 7编译器中,这是一个编译器错误,但可以理解。 Java 7 SE版的Java语言规范的第16章“明确赋值”说:

每个局部variables( §14.4 )和每个空白的final字段( §4.12.4 , §8.3.1.2 )都必须有一个明确赋值的值,

对其值的访问由简单赋值运算符的左侧操作数(除了简单赋值运算符的左侧操作数)外,在expression式的任何位置出现的variables的简单名称(或者,对于字段而言,由此限定的字段的简单名称) 15.26.1 )。

(强调我的)。 所以在expression式2 * this.xthis.x部分应该被认为是“访问[ x 's]值”,并且应该给出一个编译错误。

但是你没有问是否第一个应该编译,你问为什么编译(在一些编译器中)。 这是必然的猜测,但我会做两个猜测:

  1. 大多数Java 7编译器是通过修改Java 6编译器编写的。 一些编译器编写者可能没有注意到这种变化。 此外,许多Java-7编译器和IDE仍然支持Java 6,而且一些编译器编写者可能没有觉得有动机拒绝以Java-6模式接受的Java-7模式。
  2. 新的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特殊禁止情况是2 * obj.x ,因为(1)这个特殊的禁止情况通过添加括号很容易解决, (2)这种特殊的禁止情况在以前版本的语言中被允许,并且(3)我们仍然需要特殊的规则,即final字段具有它们的默认值(例如0代表int ),直到它们被初始化,像(this).x这样的情况,以及像this.foo()这样的情况,其中foo()是一个访问x的方法。 所以一些编译器作者可能不会有动机去做出这种不一致的变化。

其中任何一个都是令人惊讶的 – 我认为编译器编写者有关于规范的每一个变化的详细信息,并且根据我的经验,Java编译器通常相当好地坚持规范(不像某些语言,每个编译器都有它的自己的方言) – 但是,发生了什么事情,以上是我唯一的两个猜测。

当你在构造函数中使用this时, 编译器会将x看作this 对象的成员属性(默认初始化) 。 由于xint ,所以默认情况下初始化为0 。 这使编译器感到高兴,并且在运行时也能正常工作。

当你不使用this ,编译器直接在词法分析中使用x声明,因此它会抱怨初始化( 编译时现象 )。

所以它的定义是this ,它使得编译器在编译过程中的词法分析过程中将x分析为对象与直接属性的成员variables,并导致不同的编译行为。

当用作主expression式时,关键字this表示一个值,该值是对实例方法被调用的对象(第15.12节)的引用,或者是对被构造的对象的引用。

我认为编译器估计,编写this.x意味着“这个”存在,所以一个构造函数被调用(并且最终variables已经被初始化)。 但是当你试图运行它时,你应该得到一个RuntimeException

我假设你参考了Eclipse中的行为。 (正如评论一个与javac编译工程)。

我认为这是一个Eclipse问题。 它有自己的编译器和自己的一套规则。 其中之一是你可能不会访问一个没有初始化的字段,尽pipeJava-commpiler会为你初始化variables。