使用未初始化的最终字段 - 有/没有'这个'。预选赛 [英] Use of uninitialized final field - with/without 'this.' qualifier

查看:86
本文介绍了使用未初始化的最终字段 - 有/没有'这个'。预选赛的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以向我解释为什么以下两个样本中的第一个编译,而第二个没有?请注意,唯一的区别是第一个明确地使用'.this'限定对x的引用,而第二个没有。在这两种情况下,显然都会尝试在初始化之前使用最终字段x。

Can someone explain to me why the first of the following two samples compiles, while the second doesn't? Notice the only difference is that the first one explicitly qualifies the reference to x with '.this', while the second doesn't. In both cases, the final field x is clearly attempted to be used before initialized.

我原以为两个样本都会被完全相同地处理,导致编译错误两个。

I would have thought both samples would be treated completely equally, resulting in a compilation error for both.

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;
    }
}


推荐答案

之后一堆规范阅读和思考,我得出结论:

After a bunch of spec-reading and thought, I've concluded that:

在Java 5或Java 6编译器中,这是正确的行为。 第16章 Java语言规范的明确分配,第三版说:

In a Java 5 or Java 6 compiler, this is correct behavior. Chapter 16 "Definite Assignment of The Java Language Specification, Third Edition says:


每个局部变量(§14.4)和每个空白 final (§4.12.4)字段(§8.3.1.2)当对其值进行任何访问时,必须具有明确赋值值。对其值的访问包括变量的简单名称出现在表达式中的任何位置,除非简单赋值运算符的左操作数 =

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 部分被认为是<[$ c]的访问权限$ c> x 's] value(因此不受明确赋值规则的限制),因为 this.x 不是实例变量的简单名称 x 。 (注意当发生明确赋值时的规则,在上面引用的文本之后的段落中, 允许类似 this.x = 3 ,并且认为 x 之后必须明确分配;它只是不计算 this.x 的访问规则。 )请注意,在这种情况下, this.x 的值将为零,每§ 17.5.2

(emphasis mine). So in the expression 2 * this.x, the this.x part is not considered an "access of [x's] value" (and therefore is not subject to the rules of definite assignment), because this.x is not the simple name of the instance variable x. (N.B. the rule for when definite assignment occurs, in the paragraph after the above-quoted text, does allow something like this.x = 3, and considers x to be definitely assigned thereafter; it's only the rule for accesses that doesn't count this.x.) Note that the value of this.x in this case will be zero, per §17.5.2.

在Java 7中编译器,这是一个编译器错误,但是可以理解的。 Java语言的第16章定义分配规范,Java 7 SE Edition 说:

In a Java 7 compiler, this is a compiler bug, but an understandable one. Chapter 16 "Definite Assignment" of the Java Language Specification, Java 7 SE Edition says:


每个局部变量(§14.4)并且每个空白最终字段(§4.12.4§8.3.1.2)在对其值进行任何访问时,必须具有明确分配的值。

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.

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

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 的]值,应该给出编译错误。

(emphasis mine). So in the expression 2 * this.x, the this.x part should be considered an "access to [x's] value", and should give a compile error.

但你没有问 >编译(在某些编译器中)。这必然是推测性的,但我会做两个猜测:

But you didn't ask whether the first one should compile, you asked why it does compile (in some compilers). This is necessarily speculative, but I'll make two guesses:


  1. 大多数Java 7编译器是通过修改Java 6编译器编写的。一些编译器编写者可能没有注意到这种变化。此外,许多Java-7编译器和IDE仍然支持Java 6,并且一些编译器编写者可能没有动机去专门拒绝他们在Java-6模式下接受的Java-7模式。

  2. 新的Java 7行为奇怪地不一致。类似(false?null:this).x 之类的东西仍然被允许,就此而言,甚至(this).x 仍被允许;它只是特定的令牌序列加上加上受此更改影响的字段名称。当然,这种不一致性已存在于赋值语句的左侧(我们可以写 this.x = 3 ,但不能写(这).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 的方法。因此,一些编译器编写者可能没有动机去做出这种不一致的改变。

  1. Most Java 7 compilers were written by modifying Java 6 compilers. Some compiler-writers may not have noticed this change. Furthermore, many Java-7 compilers and IDEs still support Java 6, and some compiler-writers may not have felt motivated to specifically reject something in Java-7 mode that they accept in Java-6 mode.
  2. The new Java 7 behavior is strangely inconsistent. Something like (false ? null : this).x is still allowed, and for that matter, even (this).x is still allowed; it's only the specific token-sequence this plus . plus the field-name that's affected by this change. Granted, such an inconsistency already existed on the left-hand side of an assignment statement (we can write this.x = 3, but not (this).x = 3), but that's more readily understandable: it's accepting this.x = 3 as a special permitted case of the otherwise forbidden construction obj.x = 3. It makes sense to allow that. But I don't think it makes sense to reject 2 * this.x as a special forbidden case of the otherwise permitted construction 2 * obj.x, given that (1) this special forbidden case is easily worked around by adding parentheses, that (2) this special forbidden case was allowed in previous versions of the language, and that (3) we still need the special rule whereby final fields have their default values (e.g. 0 for an int) until they're initialized, both because of cases like (this).x, and because of cases like this.foo() where foo() is a method that accesses x. So some compiler-writers may not have felt motivated to make this inconsistent change.

其中任何一个都会令人惊讶—我假设编译器编写者有关于规范的每一次更改的详细信息,根据我的经验,Java编译器通常非常适合完全遵守规范(不像某些语言,每个编译器都有自己的方言)—但是,好吧,发生了一些,以上是我唯一的两个猜测。

Either of these would be surprising — I assume that compiler-writers had detailed information about every single change to the spec, and in my experience Java compilers are usually pretty good about sticking to the spec exactly (unlike some languages, where every compiler has its own dialect) — but, well, something happened, and the above are my only two guesses.

这篇关于使用未初始化的最终字段 - 有/没有'这个'。预选赛的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆