Java: definición de métodos y variables dentro de la constante de enum

Estaba haciendo algunos experimentos y accidentalmente escribí un código, que es muy extraño y no lo entiendo todo. Incluso me sorprendió poder compilarlo. Se parece a esto:

enum Foo {
    VALUE_1 {
        public int myVariable = 1;
    },
    VALUE_2 {
        public void myMethod() {
            //
        }
    },
    VALUE_3;
}

Como se esperaba, no es posible acceder a dicho elemento de la siguiente manera:

Foo.VALUE_2.myMethod();

La razón es que ese compilador buscará ese método dentro de la enumeración misma.

Supuse que no es posible acceder a estos métodos y variables desde fuera de la enumeración. Por esta razón, intenté crear un constructor paramétrico y llamarlo con alguna variable interna:

enum Foo {
    VALUE(internalVariable) {
        int internalVariable = 1;
    };

    private Foo(int param) {
        //
    }
}

No fue posible compilar tal construcción. Ahora estaba pensando cuál es el punto de definir algo dentro de la constante si no hay forma de acceder a ella.

Intenté crear los métodos con el mismo nombre en la constante también en la enumeración misma para verificar si colisiona de alguna manera. No lo hizo!

enum Foo {
    VALUE_1 {
        int myVariable = 1;

        public int myMethod() {
            return myVariable;
        }
    },
    VALUE_2 {
        //
    };

    public int myMethod() {
        return 0;
    }
}

¡Y aquí llega el momento divertido! Traté de continuar la llamada de myMethod () dentro de la enumeración y realmente descubrí cómo funciona esta magia de Java. Los métodos, que se definen dentro de la constante, anula los métodos definidos dentro de la enumeración.

Foo.VALUE_1.myMethod(); // Returns 1
Foo.VALUE_2.myMethod(); // Returns 0

Sin embargo, no podemos anular la variable, ¿verdad? Así que tenía curiosidad, cómo funciona solo con variables.

enum Foo {
    VALUE_1 {
        public int myVariable = 1;
    },
    VALUE_2 {
        //
    };

    public int myVariable = 0;
}

....

System.out.println(Foo.VALUE_1.myVariable); // Returns 0
System.out.println(Foo.VALUE_2.myVariable); // Returns 0

Ahora finalmente estoy llegando a mis preguntas:

  1. ¿Por qué no obtengo ningún error si creo un método público dentro de la enumeración constante y la dejo vacía sin este método? En ese caso, el método que acabo de definir no se puede invocar en absoluto. ¿O estoy equivocado?

    Actualización: Sé que la enumeración puede implementar la interfaz. Sin embargo, si no lo he dicho específicamente, todo el código no tiene sentido.

    Alguien señaló que incluso si no se puede acceder al método desde el lenguaje de la manera normal, aún es posible usar la reflexión. Bueno ... ¿Por qué no diseñamos una palabra clave inaccesible ?

    inaccessible void magicalMethod() {
         //
    }
    

    Dicho método se compilará en el archivo * .class. Cuando quiera usarlo, debe cargar el bytecode usted mismo e interpretarlo.

    No puedo entender por qué es posible definir un método inalcanzable. La única razón por la que puedo pensar es que el programador está funcionando y todavía no tiene una definición de interfaz. Así que solo está preparando el código de métodos individuales y luego agregará la palabra clave "implements". Además de esto, es ilógico, aún requeriría tener dicho método en todas las constantes.

    Creo que esto debería terminar con un error, no solo una advertencia sobre el método no utilizado. Puede olvidar agregar la cláusula "implemente" o definir el método en la enumeración (que se anularía) y se dará cuenta de eso justo después del primer uso. Java es un lenguaje muy estricto, por lo que esperaría este comportamiento.

  2. ¿Por qué no obtengo ningún error si creo una variable pública (o campo, para ser más precisos) dentro de la constante? No se puede acceder en ningún caso (desde el exterior). Por lo tanto, el modificador "público" no tiene ningún sentido aquí.

    Actualización: es más menos lo mismo que en el punto anterior, excepto que el modificador de visibilidad es completamente inútil aquí. Realmente no importa si es público, protegido o privado, porque de todos modos no podrá acceder a eso. Creo que esto es un error.

  3. ¿Por qué es posible definir una clase (sin modificadores de visibilidad), pero no la interfaz ? Sí, probablemente no quieras escribir una enumeración tan brutal que necesites definir clases dentro de la constante e incluso usar la herencia allí. Pero si es posible definir clases y clases abstractas, parece un poco raro.

    Actualización: Esto definitivamente no es algo que necesitarías regularmente, pero entiendo que podría ser útil. Pero, ¿por qué se limita solo a clases y las interfaces no se pueden definir también?

    enum Foo {
        VALUE {
            class MyClass {
                // OK
            }
    
            abstract class MyAbstractClass {
                // OK
            }
    
            interface MyInterface {
                // FAIL. It won't compile.
            }
        }
    }
    
  4. ¿Usaste tal funcionalidad en alguna parte? Me imagino que podría ser útil, pero es un poco confuso. Además, cuando estaba buscando algunos recursos sobre eso, no encontré nada.

    Actualización: me gustaría ver algunos ejemplos prácticos con métodos anulados en un cuerpo de clase constante enum. ¿Lo has visto en algún proyecto de código abierto?

Ambiente:

$ java -version
java version "1.7.0_21"
OpenJDK Runtime Environment (IcedTea 2.3.9) (7u21-2.3.9-0ubuntu0.12.10.1)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)

¡Gracias por su tiempo y por sus respuestas!

Respuesta 1

Por favor, consulte el siguiente código. Cuando ejecuto el código, puedo cambiar el valor de una variable final no estática. Pero si trato de cambiar el valor de una variable estática final, arroja java.lang ...

Tengo algunos problemas para encontrar un buen enfoque / implementación usando la API de Stream para lo siguiente: Tengo una lista de elementos, cada elemento que consiste en una cadena y un número entero. Ahora me gusta agrupar ...

Quiero usar una de las tareas de Ant (scp) desde mi código. ¿Hay alguna forma de hacerlo? ¿Debo simplemente hacer referencia a una de las bibliotecas de Ant y llamar a la API directamente desde mi código?

Me preguntaba cómo modificar una JList para que hacer clic en cualquier valor no sirviera de nada. He examinado otras preguntas pero ninguna me ha ayudado.