java - usar - ¿Por qué esta clase interna estática no puede llamar a un método no estático en su clase externa?
public static java (5)
¡Actualmente estoy leyendo Effective Java de Joshua Bloch y me encanta! Pero en la página 112 (artículo 24) Bloch escribe:
Una clase miembro estática es el tipo más simple de clase anidada. Es mejor concebirlo como una clase ordinaria que se declara dentro de otra clase y tiene acceso a todos los miembros de la clase que lo incluye, incluso a aquellos declarados privados.
Y eso realmente me confunde. Preferiría decir:
Una clase miembro estática es el tipo más simple de clase anidada. Es mejor concebirlo como una clase ordinaria que se declara dentro de otra clase y tiene acceso a todos los miembros estáticos de la clase delimitada , incluso a aquellos declarados privados.
Aquí hay un fragmento que ilustra mi comprensión de la cita:
public class OuterClass {
public void printMessage(String message) {
System.out.println(message);
}
private static class InnerClass {
public void sayHello() {
printMessage("Hello world!"); //error: Cannot make a static reference to the non-static method printMessage(String)
}
}
}
Puede ver que el método sayHello de InnerClass no tiene acceso al método printMessage de OuterClass como se declara en una clase interna estática, mientras que el método printMessage es un método de instancia. Parece que el autor sugiere que una clase miembro estática puede acceder a campos no estáticos de la clase adjunta. Estoy convencido de que he malinterpretado algo en su última frase, pero no puedo entender qué. Cualquier ayuda será apreciada!
Edición: Cambié la visibilidad de los dos métodos porque es irrelevante para mi pregunta. Me interesan los miembros estáticos, no los miembros privados.
A mi modo de ver, el texto es absolutamente correcto. Las clases miembro estáticas pueden acceder a los miembros privados de las clases adjuntas (más o menos). Dejame mostrarte un ejemplo:
public class OuterClass {
String _name;
int _age;
public OuterClass(String name) {
_name = name;
}
public static OuterClass CreateOuterClass(String name, int age) {
OuterClass instance = new OuterClass(name);
instance._age = age; // Notice that the private field "_age" of the enclosing class is visible/accessible inside this static method (as it would also be inside of a static member class).
return instance;
}
}
El hecho de que InnerClass
sea static
, no significa que no pueda obtener una referencia a una instancia de OuterClass
través de otros medios, generalmente como un parámetro, por ejemplo
public class OuterClass {
private void printMessage(String message) {
System.out.println(message);
}
private static class InnerClass {
private void sayHello(OuterClass outer) {
outer.printMessage("Hello world!"); // allowed
}
}
}
Si InnerClass
no hubiera sido anidado dentro de OuterClass
, no habría tenido acceso al método private
.
public class OuterClass {
private void printMessage(String message) {
System.out.println(message);
}
}
class InnerClass {
private void sayHello(OuterClass outer) {
outer.printMessage("Hello world!"); // ERROR: The method printMessage(String) from the type OuterClass is not visible
}
}
La forma en que lo mostraste requiere herencia. Pero los métodos y campos podrían ser accedidos de esta manera:
public class OuterClass {
private void printMessage(String message) {
System.out.println(message);
}
private static class InnerClass {
private void sayHello() {
OuterClass outer = new OuterClass();
outer.printMessage("Hello world!");
}
}
}
Pero, el hecho de que la clase interna estática no tenga acceso a la función printMessage no tiene que ver con eso, es una clase interna, sino que es estático y no puede invocar un método no estático. Creo que el uso de la palabra "estática" que propuso estaba implícito en la primera oración. Lo que él señala o elige enfatizar es que la clase interna todavía puede acceder a los métodos privados de su clase principal. Puede que haya pensado que es innecesario o confuso hacer la distinción estática / no estática en la misma oración, también.
Tenga en cuenta el mensaje de error. No está diciendo que no tengas acceso . Está diciendo que el método no puede ser llamado . Los métodos de instancia no significan nada sin una instancia para llamarlos. Lo que el mensaje de error le está diciendo es que no tiene esa instancia.
Lo que Bloch le está diciendo es que si esa instancia existiera, el código en la clase interna podría llamar métodos de instancia privados.
Digamos que tenemos la siguiente clase:
public class OuterClass {
public void publicInstanceMethod() {}
public static void publicClassMethod() {}
private void privateInstanceMethod() {}
private static void privateClassMethod() {}
}
Si intentamos llamar a esos métodos privados desde alguna clase aleatoria, no podemos:
class SomeOtherClass {
void doTheThing() {
OuterClass.publicClassMethod();
OuterClass.privateClassMethod(); // Error: privateClassMethod() has private access in OuterClass
}
void doTheThingWithTheThing(OuterClass oc) {
oc.publicInstanceMethod();
oc.privateInstanceMethod(); // Error: privateInstanceMethod() has private access in OuterClass
}
}
Tenga en cuenta que esos mensajes de error dicen acceso privado .
Si agregamos un método a OuterClass
, podemos llamar a esos métodos:
public class OuterClass {
// ...declarations etc.
private void doAThing() {
publicInstanceMethod(); // OK; same as this.publicInstanceMethod();
privateInstanceMethod(); // OK; same as this.privateInstanceMethod();
publicClassMethod();
privateClassMethod();
}
}
O si añadimos una clase interna estática:
public class OuterClass {
// ...declarations etc.
private static class StaticInnerClass {
private void doTheThingWithTheThing(OuterClass oc) {
publicClassMethod(); // OK
privateClassMethod(); // OK, because we''re "inside"
oc.publicInstanceMethod(); // OK, because we have an instance
oc.privateInstanceMethod(); // OK, because we have an instance
publicInstanceMethod(); // no instance -> Error: non-static method publicInstanceMethod() cannot be referenced from a static context
privateInstanceMethod(); // no instance -> Error: java: non-static method privateInstanceMethod() cannot be referenced from a static context
}
}
}
Si agregamos una clase interna no estática, parece que podemos hacer magia:
public class OuterClass {
// ...declarations etc.
private class NonStaticInnerClass {
private void doTheThing() {
publicClassMethod(); // OK
privateClassMethod(); // OK
publicInstanceMethod(); // OK
privateInstanceMethod(); // OK
}
}
}
Sin embargo, hay trucos aquí: una clase interna no estática siempre está asociada con una instancia de la clase externa, y lo que realmente estás viendo es:
private class NonStaticInnerClass {
private void doTheThing() {
publicClassMethod(); // OK
privateClassMethod(); // OK
OuterClass.this.publicInstanceMethod(); // still OK
OuterClass.this.privateInstanceMethod(); // still OK
}
}
Aquí, OuterClass.this
es una sintaxis especial para acceder a esa instancia externa. Pero solo lo necesita si es ambiguo, por ejemplo, si las clases externa e interna tienen métodos con el mismo nombre.
Tenga en cuenta también que la clase no estática todavía puede hacer lo que puede hacer la estática:
private class NonStaticInnerClass {
private void doTheThingWithTheThing(OuterClass oc) {
// ''oc'' does *not* have to be the same instance as ''OuterClass.this''
oc.publicInstanceMethod();
oc.privateInstanceMethod();
}
}
En resumen: lo public
y lo private
son siempre de acceso . El punto que Bloch está haciendo es que las clases internas tienen acceso que otras clases no tienen. Pero ninguna cantidad de acceso le permite llamar a un método de instancia sin decirle al compilador a qué instancia desea llamar.