programacion - recorrer enum java
¿Es un cuerpo de clase específica constante de enum estático o no estático? (8)
¿Cuál es el tipo de PLUS()
?
bueno, esto es básicamente el tipo de enum Operation
.
Si desea compararlo con la java class
, básicamente es un object
de la misma clase dentro de sí mismo.
Ahora la enum Operation
tiene un método abstracto que significa que cualquiera de este tipo (es decir, la operación) debe implementar este método. Hasta aquí todo bien.
ahora es la parte difícil donde obtienes el error.
como ve, PLUS()
es básicamente un tipo de Operation
. Operation
tiene un método private
printMe()
significa que solo la enum Operation
puede verla no en ninguna otra enumeración, incluidas las sub-enumeraciones (como la subclase y la superclase en java). también este método no es static
, lo que significa que no puedes llamarlo a menos que instancias la clase. entonces terminaste con dos opciones para resolver el problema,
- hacer que el
printMe() method static
como sugerencia del compilador -
protected
el método para que cualquiersub-enum
inherits
estos métodos.
Tengo una clase de tipo enum:
public enum Operation {
PLUS() {
@Override
double apply(double x, double y) {
// ERROR: Cannot make a static reference
// to the non-static method printMe()...
printMe(x);
return x + y;
}
};
private void printMe(double val) {
System.out.println("val = " + val);
}
abstract double apply(double x, double y);
}
Como puede ver arriba, he definido un tipo de enum
que tiene el valor PLUS
. Contiene un cuerpo constante específico. En su cuerpo, traté de llamar a printMe(val);
, pero obtuve el error de compilación:
No se puede hacer una referencia estática al método no estático printMe ().
¿Por qué obtengo este error? Quiero decir que estoy anulando un método abstracto en el cuerpo PLUS
. ¿Por qué está en static
alcance static
? Cómo deshacerse de él?
Sé que al agregar una palabra clave static
en printMe(){...}
resuelve el problema, pero estoy interesado en saber si hay otra manera si quiero mantener a printMe()
no estático.
Otro problema, bastante similar al anterior, pero esta vez el mensaje de error suena al revés, es decir, PLUS(){...}
tiene un contexto no estático:
public enum Operation {
PLUS() {
// ERROR: the field "name" can not be declared static
// in a non-static inner type.
protected static String name = "someone";
@Override
double apply(double x, double y) {
return x + y;
}
};
abstract double apply(double x, double y);
}
Intento declarar una variable static
PLUS
, pero termino con un error:
el campo "nombre" no se puede declarar estático en un tipo interno no estático.
¿Por qué no puedo definir la constante estática dentro de PLUS
si PLUS
es una clase anónima? Los dos mensajes de error suenan contradictorios entre sí, ya que el primer mensaje de error dice PLUS(){...}
tiene contexto estático mientras que el segundo mensaje de error dice PLUS(){...}
tiene contexto no estático . Estoy aún más confundido ahora.
Bueno, este es un caso extraño.
Parece que el problema es:
En este caso, el miembro privado debe estar accesible ( 6.6.1 .):
De lo contrario, el miembro o constructor se declara
private
, y el acceso se permite si y solo si ocurre dentro del cuerpo de la clase de nivel superior que encierra la declaración del miembro o constructor.Sin embargo, los miembros privados no son heredados ( 8.2 ):
Los miembros de una clase que se declaran
private
no son heredados por subclases de esa clase.Por lo tanto,
printMe
no es miembro de la subclase anónima y el compilador la busca dentro de la superclase *Operation
( 15.12.1 ):Si hay una declaración de tipo adjunta de la que ese método es miembro , sea T la declaración de tipo más interna. La clase o interfaz para buscar es T.
Esta política de búsqueda se llama "regla de peine". Efectivamente busca métodos en la jerarquía de superclase de una clase anidada antes de buscar métodos en una clase envolvente y su jerarquía de superclase.
Y aquí es donde se vuelve extraño. Como
printMe
se encuentra en una clase que también incluyePLUS
, se determina que el objeto en el que se llama al método es una instancia 15.12.4.1 deOperation
, que no existe ( 15.12.4.1 ):De lo contrario, supongamos que T es la declaración de tipo adjunta de la que el método es miembro, y n sea un número entero tal que T es la declaración de tipo que encierra léxicamente a la clase cuya declaración contiene inmediatamente la invocación del método. La referencia de destino es la nésima instancia léxica de
this
.Es un error en tiempo de compilación si no existe la n presente instancia léxica de
this
.
En resumen, porque printMe
es solo un miembro de Operation
(y no heredado), el compilador se ve obligado a invocar printMe
en una instancia externa inexistente .
Sin embargo, el método todavía es accesible y podemos encontrarlo calificando la invocación:
@Override
double apply(double x, double y) {
// now the superclass is searched
// but the target reference is definitely ''this''
// vvvvvv
super.printMe(x);
return x + y;
}
Los dos mensajes de error suenan contradictorios entre sí [...].
Sí, este es un aspecto confuso del lenguaje. Por un lado, una clase anónima nunca es estática ( 15.9.5 ); por otro lado, una expresión de clase anónima puede aparecer en un contexto estático y, por lo tanto, no tiene ninguna instancia 8.1.3 ( 8.1.3 ).
Una clase anónima es siempre una clase interna; nunca es
static
Una instancia de una clase interna
I
cuya declaración se produce en un contexto estático no tiene instancias léxicamente envolventes.
Para ayudar a entender cómo funciona esto, aquí hay un ejemplo formateado:
class Example { public static void main(String... args) { new Object() { int i; void m() {} }; } }
Todo en italics
es el contexto estático. La clase anónima derivada de la expresión en bold
se considera interna y no estática (pero no tiene una instancia adjunta de Example
).
Como la clase anónima no es estática, no puede declarar miembros estáticos no constantes, a pesar de que se declara en un contexto estático.
* Además de oscurecer un poco el asunto, el hecho de que Operation
es una enumeración es completamente irrelevante ( 8.9.1 ):
El cuerpo de clase opcional de una constante enum define implícitamente una declaración de clase anónima que amplía el tipo de enumeración que encierra inmediatamente. El cuerpo de la clase se rige por las reglas habituales de las clases anónimas [...].
En esta situación, la razón por la que estático funciona es la funcionalidad de acceso sintético automático. Seguirá recibiendo la siguiente advertencia del compilador si es privada.
Acceso al método de encierre printMe (doble) del tipo Operación se emula mediante un método de acceso sintético
Lo único que no funciona en este caso es privado no estático. Todas las demás obras de seguridad, p. Ej., Estática privada, protegida no estática, etc. Como alguien más mencionó que PLUS es una implementación de Operación, por lo que no funciona técnicamente de manera privada, Java automáticamente hace el favor de arreglarlo con el acceso sintético automático funcionalidad.
Hacer que el método printMe sea estático resuelve el error de compilación:
private static void printMe(long val) {
System.out.println("val = " + val);
}
La pregunta que sigue a su actualización es fácil de responder. Las clases anónimas nunca se permiten miembros estáticos.
En cuanto a su pregunta original, la forma más clara de entender lo que está sucediendo es probar this.printMe();
en lugar. Entonces, el mensaje de error es mucho más fácil de entender y da la verdadera razón printMe();
no funciona:
''printMe(double)'' has private access in ''Operation''
La razón por la que no puede usar printMe
es porque es private
y el tipo de tiempo de compilación de this
referencia es una clase de extensión anónima de Operation
, no Operation
sí (consulte la respuesta de Edwin Dalorzo). printMe();
un mensaje de error diferente cuando escribes printMe();
porque, por alguna razón, el compilador ni siquiera se da cuenta de que está tratando de invocar un método de instancia en this
. Da el mensaje de error que tendría sentido si intentas llamar a printMe
en ninguna instancia (es decir, como si fuera un método estático). El mensaje de error no cambia si lo hace explícito escribiendo Operation.printMe();
.
Dos formas de evitar esto son hacer que printMe
protegido, o escribir
((Operation) this).printMe();
Luché bastante con este, pero creo que la mejor manera de entenderlo es mirar un caso análogo que no involucre a enum
:
public class Outer {
protected void protectedMethod() {
}
private void privateMethod() {
}
public class Inner {
public void method1() {
protectedMethod(); // legal
privateMethod(); // legal
}
}
public static class Nested {
public void method2() {
protectedMethod(); // illegal
privateMethod(); // illegal
}
}
public static class Nested2 extends Outer {
public void method3() {
protectedMethod(); // legal
privateMethod(); // illegal
}
}
}
Un objeto de clase Inner
es una clase interna; cada uno de esos objetos contiene una referencia oculta a un objeto de la clase Outer
que los encierra. Es por eso que las llamadas a protectedMethod
y privateMethod
son legales. Se les llama al objeto Outer
que lo contiene, es decir hiddenOuterObject.protectedMethod()
y hiddenOuterObject.privateMethod()
.
Un objeto de clase Nested
es una clase anidada estática; no hay ningún objeto de una clase Outer
asociada con uno. Es por eso que las llamadas a protectedMethod
y privateMethod
son ilegales, no hay ningún objeto Outer
para que funcionen. El mensaje de error es non-static method <method-name>() cannot be referenced from a static context
. (Tenga en cuenta que privateMethod
aún es visible en este punto. Si method2
tenía un objeto outer
diferente de tipo Outer
, podría llamar a outer.privateMethod()
legalmente, pero en el código de ejemplo, no hay ningún objeto para que funcione).
Un objeto de la clase Nested2
es similarmente una clase anidada estática, pero con un giro que extiende Outer
. Como los miembros protegidos de Outer
se heredarán, eso hace que sea legal llamar a protectedMethod()
; funcionará en el objeto de clase Nested2
. El método privado privateMethod()
no es heredado, sin embargo. Entonces el compilador lo trata de la misma manera que lo hace con Nested
, que produce el mismo error.
El caso enum
es muy similar al caso Nested2
. Cada constante enum con un cuerpo provoca la creación de una nueva subclase anónima de Operation
, pero en realidad es una clase anidada estática (aunque las clases anónimas suelen ser clases internas). El objeto PLUS
no tiene una referencia oculta a un objeto de clase Operation
. Por lo tanto, los miembros públicos y protegidos, que se heredan, se pueden referenciar y operarán en el objeto PLUS
; pero las referencias a miembros privados en Operation
no se heredan, y no se puede acceder a ellas porque no hay ningún objeto oculto para trabajar. El mensaje de error, Cannot make a static reference to the non-static method printMe()
, es más o menos lo mismo que non-static method cannot be referenced from a static context
, solo con las palabras en un orden diferente. (No estoy afirmando que todas las reglas del lenguaje sean como el caso de Nested2
, pero en este caso, definitivamente me ayudó a verlas como casi el mismo tipo de constructo).
Lo mismo se aplica a las referencias a campos protegidos y privados.
No creo tener la respuesta sobre la naturaleza del error, pero quizás pueda contribuir un poco a la discusión.
Cuando el compilador de Java compila su código enum, produce una clase sintética que es algo así:
class Operation {
protected abstract void foo();
private void bar(){ }
public static final Operation ONE;
static {
ONE = new Operation() {
@Override
protected void foo(){
bar();
}
};
}
}
Puede verificar que el código enum se ve algo así ejecutando javap en una de sus clases enum.
Este código anterior me da exactamente el mismo error que está obteniendo en su enumeración: "error: no se puede hacer referencia a la barra de métodos no estáticos () desde un contexto estático".
Entonces, aquí el compilador cree que no puede invocar el método bar()
, que es un método de instancia, del contexto estático en el que se está definiendo la clase anónima.
No tiene sentido para mí, debe ser accesible o se le debe denegar el acceso, pero el error no parece exacto. Todavía estoy desconcertado, pero esto parece ser lo que realmente está sucediendo.
Tendría más sentido si los compiladores dijeran que la clase anónima no tuvo acceso a foo
ya que es privada en su clase principal, pero el compilador está activando este otro error.
printMe
no debe ser private
ya que deriva una nueva clase anónima con PLUS.
protected void printMe(double val) {
En cuanto a la naturaleza del error, enum / Enum, es un poco un artefacto; me escapa en este momento: una clase interna podría acceder a cosas privadas ...