java 枚举先后顺序,Java枚举属性根据访问顺序返回null

I was exploring enums in java to see how they could be abused and I came across a behaviour I couldn't explain. Consider the following class:

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);

}

}

The first statement in the main function will print 'WEED', as expected. The second one will print 'null'. However, if you switch them around, i.e.

System.out.println(ENUM.ANIMALS.DOGS.GIN.RIVAL);

System.out.println(ENUM.ANIMALS.CATS.GARFIELD.RIVAL);

the first statement will print 'FELIX' and the second statement will now print 'null'. Is there anyone that can explain this phenomenon?

For reference, I'm running the Java(TM) SE Runtime Environment (build 1.8.0_05-b13)

解决方案

This has to do with enums and class initialization.

enum SomeEnum {

CONSTANT;

}

compiles to something similar to

final class SomeEnum extends Enum {

public static final SomeEnum CONSTANT = new SomeEnum();

}

Next, execute either the class variable initializers and static

initializers of the class, or the field initializers of the interface,

in textual order, as though they were a single block.

In the following

final class SomeEnum extends Enum {

public static final SomeEnum CONSTANT = new SomeEnum();

public static final SomeEnum CONSTANT_2 = new SomeEnum();

}

CONSTANT would be initialized first, and CONSTANT_2 second.

Fourth, if a class is currently being initialized by the current thread, you proceed normally.

If the Class object for C indicates that initialization is in progress

for C by the current thread, then this must be a recursive request for

initialization. Release LC and complete normally.

How does this all come together?

This

ENUM.ANIMALS.CATS.GARFIELD.RIVAL

is evaluated like

CATS cat = ENUM.ANIMALS.CATS.GARFIELD;

DOGS rvial = cat.RIVAL;

The first access to GARFIELD forces the initialization of the enum type CATS. That begins initializing the enum constants in CATS. Compiled, those appear like

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);

These get initialized in order. So FELIX goes first. As part of its new instance creation expression, it accesses DOGS.AKAME, where the type DOGS is not yet initialized, so Java starts initializing it. The DOGS enum type, compiled, looks like

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);

So we start with GIN. In its new instance creation expression, it tries to access CATS.FELIX. CATS is current being initialized, so we just continue. CATS.FELIX hasn't been assigned a value yet. It's currently in construction lower on the stack. So its value is null. So GIN.RIVALS gets a reference to null. The same happens to all DOGS' RIVAL.

When all of the DOGS are initialized, execution returns to

private static final CATS FELIX = new CATS(DOGS.AKAME);

where DOGS.AKAME now refers to a fully initialize DOGS object. That gets assigned to its CATS#RIVAL field. Same for each of the CATS. In other words, all the CATS' RIVAL field are assigned a DOGS reference, but not the other way around.

Reordering the statements simply determines which enum type gets initialized first.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值