Java 使用未初始化的最终字段-带/不带';这是';合格者

Java 使用未初始化的最终字段-带/不带';这是';合格者,java,Java,有人能给我解释一下为什么下面两个示例中的第一个可以编译,而第二个不能编译?请注意,唯一的区别是第一个显式地用“.this”限定对x的引用,而第二个则没有。在这两种情况下,在初始化之前,显然尝试使用最终字段x 我本以为这两个样本会被完全平等地对待,从而导致两者的编译错误 (一) (二) 我认为编译器估计编写this.x意味着“this”存在,因此调用了构造函数(并且初始化了最终变量)。 但是当您在构造函数中使用this时,您在尝试运行它时应该会得到RuntimeException,编译器将x视为t

有人能给我解释一下为什么下面两个示例中的第一个可以编译,而第二个不能编译?请注意,唯一的区别是第一个显式地用“.this”限定对x的引用,而第二个则没有。在这两种情况下,在初始化之前,显然尝试使用最终字段x

我本以为这两个样本会被完全平等地对待,从而导致两者的编译错误

(一)

(二)


我认为编译器估计编写this.x意味着“this”存在,因此调用了构造函数(并且初始化了最终变量)。
但是当您在构造函数中使用
this
时,您在尝试运行它时应该会得到RuntimeException,编译器将
x
视为
this
对象(默认初始化)的成员属性。由于
x
int
,因此它默认初始化为
0
。这使编译器很高兴,并且在运行时也能正常工作

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

这就是
this
的定义,它使得编译器在编译的词法分析过程中将
x
作为对象的成员变量与直接属性进行分析,从而导致不同的编译行为

当用作主表达式时,关键字this表示引用实例方法的对象(§15.12)或正在构造的对象的值


我想您指的是Eclipse中的行为。(如注释所述,使用javacworks编译)


我认为这是一个日食问题。它有自己的编译器和自己的规则集。其中之一是,您可能无法访问未初始化的字段,尽管Java commpiler会为您初始化变量。

经过一系列规范阅读和思考,我得出结论:

在Java5或Java6编译器中,这是正确的行为。说:

每个局部变量和每个空白的
final
字段在访问其值时必须有一个明确的赋值。对其值的访问包括变量的简单名称,该变量出现在表达式中的任何位置,但作为简单赋值运算符的左操作数除外
=

(我的重点)。因此,在表达式
2*this.x
中,
this.x
部分不被视为“访问[
x
的]值”(因此不受明确赋值规则的约束),因为
this.x
不是实例变量
x
的简单名称。(注意:在上面引用的文本后面的段落中,出现明确分配时的规则确实允许类似
this.x=3
的内容,并认为此后必须明确分配
x
;只有访问规则才不计入
this.x
)请注意,在这种情况下,
this.x
的值将为零,按

在Java7编译器中,这是一个编译器错误,但可以理解。说:

每个局部变量()和每个空白的
final
字段(,)在访问其值时必须有一个明确的赋值

对其值的访问包括变量的简单名称(或者,对于字段,由
this
)限定的字段的简单名称)
出现在表达式中的任何位置,但简单赋值运算符的左操作数
=
()除外

(我的重点)。因此,在表达式
2*this.x
中,
this.x
部分应被视为“访问[
x
的]值”,并应给出编译错误

但是您没有问第一个是否应该编译,而是问它为什么要编译(在某些编译器中)。这必然是推测,但我要做两个猜测:

  • 大多数Java7编译器都是通过修改Java6编译器编写的。一些编译器编写人员可能没有注意到这一变化。此外,许多Java-7编译器和IDE仍然支持Java 6,一些编译器编写人员可能没有动机专门拒绝Java-7模式中他们在Java-6模式中接受的东西
  • 新的Java7行为奇怪地不一致。类似于
    (false?null:this).x
    的内容仍然是允许的,因此,甚至
    (this).x
    仍然是允许的;只有特定的令牌序列
    加上
    加上受此更改影响的字段名。诚然,这种不一致性已经存在于赋值语句的左侧(我们可以写
    this.x=3
    ,但不能写
    (this.x=3
    ),但这更容易理解:它接受
    this.x=3
    ,作为一种特殊的被禁止的构造
    obj.x=3
    。允许这样做是有道理的。但我认为,拒绝将
    2*this.x
    作为一个特殊的禁止使用的情况,否则允许使用
    2*obj.x
    ,是没有意义的,因为(1)这个特殊禁止使用的情况很容易通过添加括号来解决,(2)这个特殊禁止使用的情况在语言的早期版本中是允许的,以及(3)我们仍然需要一个特殊的规则,
    final
    字段在初始化之前有它们的默认值(例如
    int
    0
    ),这两种情况都是因为
    (this).x
    ,以及
    this.foo()
    其中
    foo()
    是访问
    x
    的方法。因此,一些编译器编写人员可能没有动机进行这种不一致的更改
  • 这两个中的任何一个
    public class Foo {
        private final int x;
        private Foo() {
            int y = 2 * this.x;
            x = 5;
        }
    }
    
    public class Foo {
        private final int x;
        private Foo() {
            int y = 2 * x;
            x = 5;
        }
    }