似乎我可以*在定义之前引用一个字段*

时间:2013-08-22 13:36:15

标签: java static enums declaration

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,我错了吗?

3 个答案:

答案 0 :(得分:2)

重要的JLS部分是thisthis

  

类或接口类型T将在紧接之前初始化   首次出现以下任何一种情况:

     

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

     

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

     

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

     

使用T声明的静态字段,该字段不是常量   变量(§4.12.4)。

     

T是顶级类(第7.6节)和断言语句(第14.10节)   在词典中嵌套在T(§8.1.3)中执行。

  

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

所以用

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

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

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

在撰写FOO(BAR)时,您实际上正在调用MyEnum1的构造函数,因此BAR必须评估,从那时起,这是不可能的{ {1}}尚未定义。

撰写BAR时,您创建一个名为FOO {...}的新枚举常量,但定义一个新的匿名类。由于类定义此时仅已加载('加载',如'ClassLoader'),并且尚未进行任何评估,因此不会发生错误。然后,正在创建FOO,程序的其余部分继续等,并且当您创建方法时,BAR {...}(或return BAR;)仅评估调用return FOO;,这在那一点上是完全可能的,因为两个枚举常量在这一点上都很幸福。