java - 使用未初始化的最终字段 - 带/不带 'this.' 限定符

标签 java

有人可以向我解释为什么以下两个示例中的第一个可以编译,而第二个不可以吗?请注意,唯一的区别是第一个用 '.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 编译器中,这是正确的行为。 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 部分 not 被认为是“访问 [x's] value”(因此不受明确赋值规则的约束),因为 this.x 不是实例变量 x 的简单名称。 (注意,当发生明确分配时,在上面引用的文本之后的段落中,确实允许类似 this.x = 3 的内容,并考虑 x 之后明确分配;它只是不计入 this.x 的访问规则。)注意 this.x 在 this 中的值根据 §17.5.2,case 将为零.

在 Java 7 编译器中,这是一个编译器错误,但可以理解。 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's] value",并且应该给出一个编译错误。

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

  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 编译器通常非常擅长完全遵守规范(不像某些语言,每个编译器有自己的方言)——但是,发生了一些事情,以上只是我的两个猜测。

关于java - 使用未初始化的最终字段 - 带/不带 'this.' 限定符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13864464/

相关文章:

java - 为什么 Intellij IDEA 不显示𝔊符号?

java 泛型 : runtime type-checking to determine a strategy

java - 带有正则表达式的 Apache VFS resolveFile

java - 将结果集数据绑定(bind)到Getter Setter方法形成JSON字符串

java - 在 JSP/Servlet 中安全地传递参数(无框架)

java - Spring Boot、JPA 和 MySQL 8 方言错误

javascript - 无法接收从服务器到客户端的语言文本

java - 数组文本字段

java - 如何从麦克风录制声音?

java - 区分多个附加的 gwt 小部件的事件