Java枚举属性根据访问顺序返回null [英] Java enum attributes returning null based on order of access

查看:707
本文介绍了Java枚举属性根据访问顺序返回null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在探索java中的枚举,看看他们是如何被滥用的,我遇到了一个我无法解释的行为。考虑下列课程:

  public class PROGRAM {

public enum ENUM {;
public enum ANIMALS {;
public enum CATS {
FELIX(DOGS.AKAME),
GARFIELD(DOGS.WEED),
BUBSY(DOGS.GIN);

CATS(DOGS狗){this.RIVAL = dog;}
public DOGS RIVAL;
}
public enum DOGS {
GIN(CATS.FELIX),WEED(CATS.BUBSY),AKAME(CATS.GARFIELD);

DOGS(CATS猫){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运行时环境(build 1.8.0_05-b13)

解决方案

这与枚举和类初始化有关。



首先, code>枚举只是一个有着恒定字段的花式那就是你的枚举常数声明实际上只是 static fields。所以

 枚举SomeEnum {
CONSTANT;
}

编译为类似于

  final class SomeEnum extends Enum< SomeEnum> {
public static final SomeEnum CONSTANT = new SomeEnum();
}

其次, static 字段在左侧初始化


接下来,执行类变量初始化和静态
初始化器的类或界面的字段初始化器
在文本顺序中,就好像它们是单个块。


在以下

  final class SomeEnum extends Enum< SomeEnum> {
public static final SomeEnum CONSTANT = new SomeEnum();
public static final SomeEnum CONSTANT_2 = new SomeEnum();
}

CONSTANT 将会首先初始化,而 CONSTANT_2 秒。



第三,一个枚举类型将被[初始化] [3]当你访问其一个常量(这只是一个 static 字段)



第四,如果当前线程正在初始化一个课程,则会正常进行。


如果 Class 对象 C 表示初始化正在进行
for C 由当前线程,那么这必须是
初始化的递归请求。发布 LC 并正常完成。


/ p>

这个

  ENUM.ANIMALS.CATS.GARFIELD.RIVAL 

被评估为

  CATS cat = ENUM.ANIMALS.CATS.GARFIELD; 
DOGS rvial = cat.RIVAL;

第一次访问 GARFIELD 强制初始化的枚举键入 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 ,其中类型 DOGS 尚未初始化,所以Java开始初始化它。 $ code> DOGS 枚举类型,编译后,看起来像

  private static final DOGS GIN =新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 的引用。所有 DOGS ' RIVAL



当所有 DOGS 被初始化时,执行返回到

  private static最终CATS FELIX =新CATS(DOGS.AKAME); 

其中 DOGS.AKAME 现在指的是完全初始化 DOGS 对象。它被分配到它的 CATS#RIVAL 字段。每个 CATS 相同。换句话说,所有 CATS ' RIVAL 字段分配了一个 DOGS 引用,但不是其他方式。



重新排序语句只是确定哪个枚举类型被初始化首先。


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.

First, enum is just a fancy class with constant fields. That is, the enum constants you declare are in reality just static fields. So

enum SomeEnum {
    CONSTANT;
}

compiles to something similar to

final class SomeEnum extends Enum<SomeEnum> {
    public static final SomeEnum CONSTANT = new SomeEnum();
}

Second, static fields are initialized in the left to right order they appear in the source code.

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<SomeEnum> {
    public static final SomeEnum CONSTANT = new SomeEnum();
    public static final SomeEnum CONSTANT_2 = new SomeEnum();
}

CONSTANT would be initialized first, and CONSTANT_2 second.

Third, an enum type will be [initialized][3] when you access one of its constants (which is really just a static field).

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.

这篇关于Java枚举属性根据访问顺序返回null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆