正如人们所期望的那样,这无法编译(出现illegal forward reference
错误):
class test {
int x = x + 42;
}
但这有效:
class test {
int x = this.x + 42;
}
这是怎么回事?在后一种情况下分配了什么?
正如人们所期望的那样,这无法编译(出现illegal forward reference
错误):
class test {
int x = x + 42;
}
但这有效:
class test {
int x = this.x + 42;
}
这是怎么回事?在后一种情况下分配了什么?
x
在 x 的初始化期间发现和禁止所有访问太难了。例如
int x = that().x; | int x = getX();
|
Test that(){ return this; } | int getX(){ return x; }
该规范停留在“通过简单名称访问”,并没有尝试更全面。
在另一部分“明确分配”中,规范做了类似的事情。例如
public class Test
{
static final int y;
static final int z = y; // fail, y is not definitely assigned
static{ y = 1; }
}
public class Test
{
static final int y;
static final int z = Test.y; // pass... because it's not a simple name
static{ y = 1; }
}
有趣的是,“Definite Assignment”特别提到这this.x
相当于x
(或者,对于一个字段,由这个限定的字段的简单名称)
该条款也可以添加到 NPE 引用的部分。
- 用法是通过一个简单的名称(或由此限定的简单名称)
但最后,不可能在编译时分析所有可能的使用/访问一个字段。
摘要:两个初始化器都访问一个尚未初始化的字段(因此仍然具有默认值零)。由于这很可能是编程错误,因此该语言禁止某些简单形式的此类访问。但是,它并没有禁止更复杂的形式。
该行为符合 JLS,特别是§8.3.2.3。初始化期间使用字段的限制
仅当成员是
static
类或接口的实例(分别)字段C
并且以下所有条件都成立时,成员的声明才需要在使用之前以文本形式出现:
该用法发生在 C 的实例(分别
static
)变量初始化器或 C 的实例(分别static
)初始化器中。用法不在作业的左侧。
用法是通过一个简单的名称。
C
是包含用法的最里面的类或接口。
第一个示例满足所有四个条件,因此无效。第二个示例不满足第三个条件(this.x
不是一个简单的名称),因此可以。
事件的总体顺序如下:
因此,如果初始化器引用稍后出现在类定义中的字段(或字段本身),它将看到该其他字段的默认值。这可能是一个编程错误,因此第 8.3.2.3 节明确禁止。
例如,如果您通过使用this.
to 前向引用字段来规避 §8.3.2.3,您将看到默认值(零表示int
)。因此,以下是明确定义的,并保证设置x
为42
:
class test {
int x = this.x + 42;
}
在第一种情况下,编译器尝试评估表达式 'x + 42' 但由于 x 未初始化而失败。
在第二种情况下,表达式 'this.x + 42' 在运行时计算(因为有 'this' 关键字),此时 x 已经初始化并且值为 0。