35

正如人们所期望的那样,这无法编译(出现illegal forward reference错误):

class test {
    int x = x + 42;
}

但这有效:

class test {
    int x = this.x + 42;
}

这是怎么回事?在后一种情况下分配了什么?

4

3 回答 3

18

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 引用的部分。

  • 用法是通过一个简单的名称(或由此限定的简单名称)

但最后,不可能在编译时分析所有可能的使用/访问一个字段。

于 2013-04-04T21:05:43.243 回答
17

摘要:两个初始化器都访问一个尚未初始化的字段(因此仍然具有默认值零)。由于这很可能是编程错误,因此该语言禁止某些简单形式的此类访问。但是,它并没有禁止更复杂的形式。

该行为符合 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)。因此,以下是明确定义的,并保证设置x42

class test {
    int x = this.x + 42;
}
于 2013-04-04T19:58:24.153 回答
1

在第一种情况下,编译器尝试评估表达式 'x + 42' 但由于 x 未初始化而失败。

在第二种情况下,表达式 'this.x + 42' 在运行时计算(因为有 'this' 关键字),此时 x 已经初始化并且值为 0。

于 2013-04-05T08:40:16.680 回答