使用未初始化的最终字段 - 有/没有'这个'.预选赛

wei*_*esr 54 java

有人可以向我解释为什么以下两个样本中的第一个编译,而第二个没有?注意唯一的区别是第一个明确地使用'.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]值",并且应该给出编译错误.

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

  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,因为(1)这个特殊的禁止案件很容易通过添加括号来解决,(2)允许这种特殊的禁止案件语言的早期版本,以及(3)我们仍然需要特殊的规则,使final字段有其默认值(例如0一个int),直到他们被初始化,一方面是因为样病例的(this).x,因为像的情况下,并this.foo()在那里foo()是一种方法,访问x.因此,一些编译器编写者可能没有动力做出这种不一致的改变.

这些都是令人惊讶的 - 我认为编译器编写者有关于规范的每一次更改的详细信息,根据我的经验,Java编译器通常非常适合完全遵守规范(不像某些语言,每个编译器都有它的自己的方言) - 但是,事情发生了,以上是我唯一的两个猜测.

  • 这是http://stackoverflow.com/questions/13778650的双重问题 (3认同)