我正在探索java中的枚举,看看它们是如何被滥用的,我遇到了一种我无法解释的行为。考虑以下课程:
public class PROGRAM {
public enum ENUM {;
public enum ANIMALS {;
public enum CATS {
FELIX(DOGS.AKAME),
GARFIELD(DOGS.WEED),
BUBSY(DOGS.GIN);
CATS(DOGS dog) {this.RIVAL = dog;}
public DOGS RIVAL;
}
public enum DOGS {
GIN(CATS.FELIX), WEED(CATS.BUBSY), AKAME(CATS.GARFIELD);
DOGS(CATS cat) {this.RIVAL = cat;}
public CATS RIVAL;
}
}
}
public static void main(String[] args) {
System.out.println(ENUM.ANIMALS.CATS.GARFIELD.RIVAL);
System.out.println(ENUM.ANIMALS.DOGS.GIN.RIVAL);
}
}
主函数中的第一个语句将按预期打印'WEED'。第二个将打印'null'。但是,如果你转换它们,即
System.out.println(ENUM.ANIMALS.DOGS.GIN.RIVAL);
System.out.println(ENUM.ANIMALS.CATS.GARFIELD.RIVAL);
第一个语句将打印'FELIX',第二个语句现在将打印'null'。有没有人可以解释这种现象?
供参考,我正在运行Java(TM)SE运行时环境(版本1.8.0_05-b13)
答案 0 :(得分:2)
这与枚举和类初始化有关。
首先,enum
只是一个带有常量字段的花哨class
。 That is, the enum constants you declare are in reality just static
fields.所以
enum SomeEnum {
CONSTANT;
}
编译为类似于
的内容final class SomeEnum extends Enum<SomeEnum> {
public static final SomeEnum CONSTANT = new SomeEnum();
}
其次,static
fields are initialized in the left to right order they appear in the source code.
接下来,执行类变量初始值设定项和静态 类的初始值设定项,或接口的字段初始值设定项, 按照文字顺序,好像它们只是一个块。
以下
final class SomeEnum extends Enum<SomeEnum> {
public static final SomeEnum CONSTANT = new SomeEnum();
public static final SomeEnum CONSTANT_2 = new SomeEnum();
}
CONSTANT
将首先初始化,CONSTANT_2
秒。
第四,如果当前线程正在初始化一个类,则可以正常进行。
如果
Class
的{{1}}对象表示正在进行初始化 对于当前线程的C
,那么这必须是递归请求 初始化。发布C
并正常完成。
这一切如何结合在一起?
此
LC
评估为
ENUM.ANIMALS.CATS.GARFIELD.RIVAL
首次访问CATS cat = ENUM.ANIMALS.CATS.GARFIELD;
DOGS rvial = cat.RIVAL;
会强制GARFIELD
类型enum
的初始化。这开始初始化CATS
中的枚举常量。编译,那些看起来像
CATS
这些按顺序初始化。所以private static final CATS FELIX = new CATS(DOGS.AKAME);
private static final CATS GARFIELD = new CATS(DOGS.WEED);
private static final CATS BUBSY = new CATS(DOGS.GIN);
首先出现。作为其新实例创建表达式的一部分,它访问FELIX
,其中类型DOGS.AKAME
尚未初始化,因此Java开始初始化它。编译的DOGS
枚举类型看起来像
DOGS
所以我们从private static final DOGS GIN = new DOGS(CATS.FELIX);
private static final DOGS WEED = new DOGS(CATS.BUBSY);
private static final DOGS AKAME = new DOGS(CATS.GARFIELD);
开始。在其新实例创建表达式中,它尝试访问GIN
。 CATS.FELIX
当前正在初始化,所以我们继续。 CATS
尚未分配值。它目前正在建设中较低的堆栈。所以它的值是CATS.FELIX
。因此null
会引用GIN.RIVALS
。所有null
&#39;都会发生同样的情况。 DOGS
。
当初始化了所有RIVAL
时,执行返回
DOGS
其中private static final CATS FELIX = new CATS(DOGS.AKAME);
现在指的是完全初始化DOGS.AKAME
对象。它被分配到DOGS
字段。每个CATS#RIVAL
都相同。换句话说,所有CATS
&#39; CATS
字段被分配了RIVAL
引用,但不是相反。
重新排序语句只是确定首先初始化哪个DOGS
类型。
答案 1 :(得分:1)
当您致电ENUM.ANIMALS.CATS.GARFIELD.RIVAL
时,它将从创建CATS枚举开始。在处理第一个元素FELIX时,它需要创建DOGS枚举,以便DOGS.AKAME可以作为参数传递给CATS构造函数。
DOGS构造函数接收CATS类型的参数,但由于CATS尚未初始化,所有CATS.something都将返回null
,因此为DOGS枚举中的所有元素设置RIVAL属性为null
当创建所有DOGS元素时,它将返回CATS并继续创建其元素,将刚刚创建的DOGS元素作为参数传递。
类似地,当您通过创建DOGS枚举来反转调用的顺序时,会导致CATS元素RIVAL属性设置为null
。
如果不清楚,请尝试使用在枚举元素中设置的断点运行代码。声明和构造者更好地理解它。