with values valueof recorrer que enum clase java enums abuse

values - recorrer enum java



Los atributos enum de Java devuelven nulo según el orden de acceso (2)

Cuando llame a ENUM.ANIMALS.CATS.GARFIELD.RIVAL , comenzará creando la enumeración CATS. Al procesar el primer elemento, FELIX, necesita crear la enumeración DOGS para que DOGS.AKAME pueda pasarse como un parámetro al constructor CATS.

El constructor DOGS recibe un parámetro de tipo CATS, pero como CATS aún no se había inicializado, todos los CATS.something devolverán null , estableciendo así el atributo RIVAL en null para todos los elementos en la enumeración DOGS.

Cuando se crean todos los elementos de DOGS, vuelve a CATS y reanuda la creación de sus elementos, pasando los elementos DOGS recién creados como parámetros.

De manera similar, cuando inviertes el orden de las llamadas, comienza creando la enumeración DOGS que causa que el atributo RIVAL de los elementos CATS se establezca como null .

Si esto no está claro, intente ejecutar su código con los puntos de interrupción establecidos en las declaraciones de los elementos enum y en los constructores para comprenderlo mejor.

Estaba explorando enums en Java para ver cómo podían ser abusados ​​y encontré un comportamiento que no podía explicar. Considera la siguiente clase:

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

La primera instrucción en la función principal imprimirá ''WEED'', como se esperaba. El segundo imprimirá ''nulo''. Sin embargo, si los cambias, es decir,

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

la primera instrucción imprimirá ''FELIX'' y la segunda instrucción ahora imprimirá ''nulo''. ¿Hay alguien que pueda explicar este fenómeno?

Como referencia, estoy ejecutando Java (TM) SE Runtime Environment (compilación 1.8.0_05-b13)


Esto tiene que ver con las enumeraciones y la inicialización de clase.

Primero, enum es solo una class elegante con campos constantes. Es decir, las constantes de enum que declaras son en realidad solo campos static . Asi que

enum SomeEnum { CONSTANT; }

compila a algo similar a

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

En segundo lugar, static campos static se inicializan en el orden de izquierda a derecha que aparecen en el código fuente.

A continuación, ejecute los inicializadores de variables de clase y los inicializadores estáticos de la clase, o los inicializadores de campo de la interfaz, en orden textual, como si fueran un solo bloque.

En el siguiente

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

CONSTANT se inicializará primero y CONSTANT_2 segundo lugar.

En tercer lugar, un tipo enum se [inicializará] [3] cuando acceda a una de sus constantes (que en realidad es solo un campo static ) .

En cuarto lugar, si una clase está siendo inicializada por el hilo actual, procede normalmente.

Si el objeto de Class para C indica que la inicialización está en curso para C por el hilo actual, entonces esta debe ser una solicitud recursiva para la inicialización. Libera LC y completa normalmente.

¿Cómo se une todo esto?

Esta

ENUM.ANIMALS.CATS.GARFIELD.RIVAL

es evaluado como

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

El primer acceso a GARFIELD fuerza la inicialización del tipo enum CATS . Eso comienza a inicializar las constantes enum en CATS . Compilado, esos aparecen como

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

Estos se inicializan en orden. Entonces FELIX va primero. Como parte de su nueva expresión de creación de instancia, accede a DOGS.AKAME , donde el tipo DOGS aún no se ha inicializado, por lo que Java comienza a inicializarlo. El tipo de enumeración DOGS , compilado, se parece a

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

Entonces comenzamos con GIN . En su nueva expresión de creación de instancia, intenta acceder a CATS.FELIX . CATS está siendo inicializado, así que simplemente continuamos. CATS.FELIX todavía no se le ha asignado un valor. Actualmente está en construcción más abajo en la pila. Entonces su valor es null . Entonces GIN.RIVALS obtiene una referencia a null . Lo mismo ocurre con el RIVAL DOGS .

Cuando todos los DOGS se inicializan, la ejecución vuelve a

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

donde DOGS.AKAME ahora se refiere a un objeto DOGS completamente inicializado. Eso se asigna a su campo CATS#RIVAL . Lo mismo para cada uno de los CATS . En otras palabras, a todos los CATS '' RIVAL field se les asigna una referencia de DOGS , pero no al revés.

Reordenar las instrucciones simplemente determina qué tipo de enum se inicializa primero.