5
public enum MyEnum1 {

    FOO(BAR), BAR(FOO);

    private MyEnum1 other;

    private MyEnum1(MyEnum1 other) {
        this.other = other;
    }

    public MyEnum1 getOther() {
        return other;
    }

}

MyEnum1生成错误Cannot reference a field before it is defined,这是可以理解的,因为声明顺序在这里很重要。但是为什么下面会编译?

public enum MyEnum2 {

    FOO { public MyEnum2 getOther() { return BAR; } },
    BAR { public MyEnum2 getOther() { return FOO; } };

    public abstract MyEnum2 getOther();

}

FOO指的是BAR之前BAR定义的,我错了吗?

4

3 回答 3

2

重要的 JLS 部分是这个这个

类或接口类型 T 将在以下任何一项第一次出现之前立即初始化:

T 是一个类,并创建了一个 T 的实例。

T 是一个类,并且调用了 T 声明的静态方法。

分配了一个由 T 声明的静态字段。

使用了由 T 声明的静态字段,并且该字段不是常量变量(第 4.12.4 节)。

T 是一个顶级类(第 7.6 节),并且执行在词法上嵌套在 T(第 8.1.3 节)中的断言语句(第 14.10 节)。

枚举常量的可选类主体隐式定义了一个匿名类声明(第 15.9.5 节),该声明扩展了直接封闭的枚举类型。

所以随着

FOO { public MyEnum2 getOther() { return BAR; } }, 
BAR { public MyEnum2 getOther() { return FOO; } };

您正在创建两个匿名类扩展MyEnum2.

BAR您调用Foo.getOther()或其他一些代码时最终引用时MyEnum2.Bar,类型将被初始化。

于 2013-08-22T14:00:14.940 回答
1

在第一种情况下,您正在参考尚未声明的常量创建枚举常量。在第二种情况下,由于编译顺序无关紧要,枚举常量在枚举体之前编译。我会说这就是原因。如果不是这样,编译会提前失败,因为抽象方法声明是在每个枚举常量主体中的非抽象方法声明之后定义的。

很好的参考 - http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9

于 2013-08-22T13:47:15.437 回答
1

在编写时FOO(BAR),您实际上是在调用的构造函数,MyEnum1因此BAR必须进行评估,这在当时是不可能的,因为BAR尚未定义。

编写时FOO {...},您正在创建一个名为 的新枚举常量FOO,但定义了一个新的匿名类。由于此时仅加载类定义(“加载”,如“ClassLoader”中的“加载”)并且尚未评估任何内容,因此不会发生错误。然后,BAR {...}正在创建,您的程序的其余部分继续,等等,并且return BAR;(或return FOO;)仅在您对 进行方法调用时才被评估getOther(),这在那时是完全可能的,因为此时两个枚举常量都快乐地活着。

于 2013-08-22T13:58:52.340 回答