java - interfaz - ¿Por qué las implementaciones de enumeración no pueden acceder a campos privados en la clase de enumeración?
jframe en java (4)
Acabo de responder a esta pregunta diciendo cómo resolver el problema de compilación:
¿Cómo usar los campos en enumeración de java anulando el método?
Pero lo que no entiendo es por qué el error está ocurriendo en primer lugar.
Aquí está el ejemplo escrito como una enumeración:
public enum MyEnum {
FIRST {
@Override
public String doIt() {
return "1: " + someField; //error
}
},
SECOND {
@Override
public String doIt() {
return "2: " + super.someField; //no error
}
};
private String someField;
public abstract String doIt();
}
Aquí es exactamente lo mismo que las clases abstractas
abstract class MyClass {
class FIRST extends MyClass {
@Override
public String doIt() {
return "1: " + someField; //no error
}
};
class SECOND extends MyClass {
@Override
public String doIt() {
return "2: " + super.someField; //no error
}
};
private String someField;
public abstract String doIt();
}
En el caso de FIRST
dentro de la implementación de la enum
, no puede acceder a someField
. Sin embargo, en el caso de la clase abstracta puede.
Además, agregar super
arreglos al problema, al igual que quitar el modificador private
en el campo.
¿Alguien sabe por qué esta ligera peculiaridad en el comportamiento está sucediendo?
Cuando se resuelve un identificador, Java prefiere el ámbito léxico sobre los miembros heredados. Entonces, cuando tiene una clase interna que extiende la clase externa y usa un campo de la clase externa sin usar this
o super
, se accede al campo de la instancia externa, que falla si la clase interna es static
ya que no hay instancia externa. Por el contrario, cuando usa super
, está accediendo explícitamente al miembro heredado. Tenga en cuenta que las clases enum
son implícitamente static
Incluso puede usar this
para acceder al miembro heredado, pero tiene que usar ((MyClass)this).someField
para acceder si se declara private
.
La clase FIRST
es una clase interna de MyClass
y también una subclase. La razón por la que no ve un error al acceder a someField
es porque está accediendo al campo someField
de la clase externa, no a la súper clase.
class MyClass {
class FIRST extends MyClass {
@Override
public String doIt() {
super.someField = "super";
return "1: " + someField;
}
};
private String someField = "outer";
public String doIt(){return "";}
public static void main(String[] args) {
System.out.println(new MyClass().new FIRST().doIt());
}
}
Impresiones 1: outer
.
En el otro caso, sus constantes de enumeración se comportan como subclases anidadas estáticas, no como clases internas, por lo que no tienen una referencia a la clase externa, solo su súper clase.
No estoy de acuerdo con la respuesta aceptada.
La declaración de enum const es public static final
implícita, pero no la clase a la que pertenece enum const.
El cuerpo de clase opcional de una constante de enumeración define implícitamente una declaración de clase anónima (§15.9.5) que extiende el tipo de enumeración que lo encierra de inmediato. El cuerpo de la clase se rige por las reglas habituales de las clases anónimas.
¿Y cuáles son las ''reglas de las clases anónimas''?
El compilador de Java deriva automáticamente una declaración de clase anónima de una expresión de creación de instancia de clase.
Una clase anónima nunca es abstracta (§8.1.1.1).
Una clase anónima siempre es implícitamente final (§8.1.1.2).
Una clase anónima es siempre una clase interna (§8.1.3); nunca es estático (§8.1.1, §8.5.1).
Y si la clase equivalente enum es una clase estática, ¿cómo explicar el siguiente error?
public enum MyClass {
First {
public static int b; //(2)Illegal static declaration in inner class
};
}
Pero, ¿por qué una clase interna no puede acceder al campo de la clase externa?
Una posible clase equivalente de enumeración puede verse como la siguiente, lo que da el mismo error que una clase de enumeración:
abstract class MyClass {
private int someField;
static {
class First extends MyClass {
public void method() {
System.out.println(someField);
}
private static int b;
}
}
}
Más:
Su clase abstracta no es equivalente a su enumeración, ya que las enumeraciones son implícitamente públicas final estática. Por lo tanto, observarás el mismo comportamiento si usas:
abstract class MyClass {
static class FIRST extends MyClass {
@Override
public String doIt() {
return "1: " + someField; // error
}
};
static class SECOND extends MyClass {
@Override
public String doIt() {
return "2: " + super.someField; // no error
}
};
private String someField;
public abstract String doIt();
}
Como se explica en http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html , capítulo "Clases anidadas estáticas":
Una clase anidada estática no puede referirse directamente a las variables de instancia o métodos definidos en su clase adjunta: puede usarlas solo a través de una referencia de objeto.
De ahí la necesidad de super
. También puede usar this
si el campo estuviera protected
lugar de private
.