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部分被认为是“访问[x的]值”(因此不受明确分配规则的约束),因为this.x不是实例变量的简单名称 x . (注意,明确分配发生时的规则,在上面引用的文本之后的段落中,确实允许类似 this.x = 3 的东西,并认为 x 此后被明确分配;这只是规则对于不计入 this.x 的访问。)请注意 this.x 的值在这种情况下将为零,根据 §17.5.2 .

在 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的]值”,并且应该给出一个编译错误。

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

  1. 大多数 Java 7 编译器都是通过修改 Java 6 编译器编写的。一些编译器编写者可能没有注意到这一变化。此外,许多 Java-7 编译器和 IDE 仍然支持 Java 6,一些编译器编写者可能没有动力明确拒绝他们在 Java-6 模式下接受的 Java-7 模式下的某些内容。
  2. 新的 Java 7 行为出奇地不一致。类似于 (false ? null : this).x仍然允许,就此而言,甚至 (this).x仍然允许;它只是特定的 token 序列 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/29583554/

相关文章:

Java 将 double 转换为 int

java - 将 Web 应用程序连接到离线编译器 (Java/Python)

java - 在 FIX42 中重建 QuickfixJ-ExecutionReport 时出现问题

java - 当一个对象被分配给另一个对象时会发生什么

java - 出现奇怪的 java.lang.NoClassDefFoundError 异常

java - 使用 ArrayUtils 将包装类数组转换为原始数组

java - 对于参数类型 boolean、double,运算符 > 未定义

java - 如何从php页面获取数据(没有json)?

java - 使用 Querydsl 过滤键值对

java - 为什么当我创建一个新 Activity 时, "setContentView"无法识别我的布局?