有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java使用带/不带“this”的未初始化最终字段合格者

有人能给我解释一下为什么下面两个示例中的第一个可以编译,而第二个不能编译?请注意,唯一的区别是,第一个显式地将对x的引用限定为'。这是',而第二个不是。在这两种情况下,在初始化之前显然尝试使用最终字段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;
    }
}

共 (4) 个答案

  1. # 1 楼答案

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

    在Java5或Java6编译器中,这是正确的行为Chapter 16 "Definite Assignment of The Java Language Specification, Third Edition说:

    Each local variable (§14.4) and every blank final (§4.12.4) field (§8.3.1.2) must have a definitely assigned value when any access of its value occurs. An access to its value consists of the simple name of the variable occurring anywhere in an expression except as the left-hand operand of the simple assignment operator =.

    (我的重点)。因此,在表达式2 * this.x中,this.x部分被认为不是“对[x值的访问”(因此不受明确赋值规则的约束),因为this.x不是实例变量x的简单名称。(注意:在上面引用的文本后面的段落中,允许像this.x = 3这样的东西,并且认为x在此后被明确分配;这只是不计算this.x的访问规则。)注意,在这种情况下,this.x的值将为零,每§17.5.2

    在Java7编译器中,这是一个编译器错误,但可以理解Chapter 16 "Definite Assignment" of the Java Language Specification, Java 7 SE Edition说:

    Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

    An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (§15.26.1).

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

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

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

    其中任何一种都是令人惊讶的——我假设编译器编写者对规范的每一次更改都有详细的信息,根据我的经验,Java编译器通常非常擅长严格遵守规范(不像某些语言,每个编译器都有自己的方言)——但是,嗯,发生了一些事情,以上是我仅有的两个猜测

  2. # 2 楼答案

    我想编译器估计写这篇文章会很困难。x表示“this”存在,因此调用了构造函数(并且初始化了最终变量)。 但是当您尝试运行它时,应该得到一个RuntimeException

  3. # 3 楼答案

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

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

  4. # 4 楼答案

    在构造函数中使用this时,编译器将x视为this对象(默认初始化)的成员属性。由于xint,因此它默认使用0初始化。这使编译器很高兴,并且在运行时也能正常工作

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

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

    When used as a primary expression, the keyword this denotes a value that is a reference to the object for which the instance method was invoked (§15.12), or to the object being constructed.