¿Por qué las clases externas de Java pueden acceder a miembros privados de clase interna?

Observé que las clases externas pueden acceder a las variables de instancia privadas de las clases internas. ¿Cómo es esto posible? Aquí hay un código de muestra que demuestra lo mismo:

class ABC{ class XYZ{ private int x=10; } public static void main(String... args){ ABC.XYZ xx = new ABC().new XYZ(); System.out.println("Hello :: "+xx.x); ///Why is this allowed?? } } 

¿Por qué se permite este comportamiento?

La clase interna es solo una manera de separar limpiamente alguna funcionalidad que realmente pertenece a la clase externa original. Están destinados a ser utilizados cuando tienes 2 requisitos:

  1. Alguna funcionalidad en su clase externa sería más clara si se implementara en una clase separada.
  2. Aunque está en una clase separada, la funcionalidad está muy ligada a la forma en que funciona la clase externa.

Dados estos requisitos, las clases internas tienen pleno acceso a su clase exterior. Como son básicamente un miembro de la clase externa, tiene sentido que tengan acceso a métodos y atributos de la clase externa, incluidos los privados.

Si desea ocultar los miembros privados de su clase interna, puede definir una interfaz con los miembros públicos y crear una clase interna anónima que implemente esta interfaz. Ejemplo abajo:

 class ABC{ private interface MyInterface{ void printInt(); } private static MyInterface mMember = new MyInterface(){ private int x=10; public void printInt(){ System.out.println(String.valueOf(x)); } }; public static void main(String... args){ System.out.println("Hello :: "+mMember.x); ///not allowed mMember.printInt(); // allowed } } 

La clase interna es (para fines de control de acceso) considerada como parte de la clase contenedora. Esto significa acceso completo a todos los privados.

La forma en que esto se implementa es usar métodos sintéticos protegidos por paquetes: la clase interna se comstackrá en una clase separada en el mismo paquete (ABC $ XYZ). La JVM no admite este nivel de aislamiento directamente, por lo que en el bytecode-level ABC $ XYZ tendrá métodos protegidos por paquetes que la clase externa utilizará para acceder a los métodos / campos privados.

Hay una respuesta correcta que aparece en otra pregunta similar a esta: ¿Por qué se puede acceder al miembro privado de una clase anidada por los métodos de la clase adjunta?

Dice que hay una definición de scope privado en JLS: determinación de la accesibilidad :

De lo contrario, si el miembro o el constructor se declara privado, se permite el acceso si y solo si ocurre dentro del cuerpo de la clase de nivel superior (§7.6) que incluye la statement del miembro o constructor.

Un caso de uso importante en mi humilde opinión para las clases internas es el patrón de fábrica. La clase adjunta puede preparar una instancia de la clase interna sin restricciones de acceso y pasar la instancia al mundo exterior, donde se respetará el acceso privado.

En contradicción con abyx declarar que la clase estática no cambia las restricciones de acceso a la clase adjunta, como se muestra a continuación. También están funcionando las restricciones de acceso entre clases estáticas en la misma clase adjunta. Me sorprendió …

 class MyPrivates { static class Inner1 { private int test1 = 2; } static class Inner2 { private int test2 = new Inner1().test1; } public static void main(String[] args) { System.out.println("Inner : "+new Inner2().test2); } } 

Las restricciones de acceso se realizan por clase. No hay forma de que un método declarado en una clase no pueda acceder a todos los miembros de instancia / clase. Es lógico que las clases internas también tengan acceso irrestricto a los miembros de la clase externa, y que la clase externa tenga acceso irrestricto a los miembros de la clase interna.

Al poner una clase dentro de otra clase, la haces estrechamente vinculada a la implementación, y todo lo que sea parte de la implementación debería tener acceso a las otras partes.

La lógica detrás de las clases internas es que si creas una clase interna en una clase externa, es porque necesitarán compartir algunas cosas, y por lo tanto tiene sentido que puedan tener más flexibilidad que las clases “normales”.

Si, en su caso, no tiene sentido que las clases puedan ver el funcionamiento interno del otro, lo que básicamente significa que la clase interna podría haberse convertido simplemente en una clase regular, puede declarar la clase interna como static class XYZ . Usar static significa que no compartirán el estado (y, por ejemplo, el new ABC().new XYZ() no funcionará, y necesitará usar el new ABC.XYZ() .
Pero, si ese es el caso, debería pensar si XYZ realmente debería ser una clase interna y si tal vez merece su propio archivo. A veces tiene sentido crear una clase interna estática (por ejemplo, si necesita una clase pequeña que implemente una interfaz que está utilizando su clase externa, y que no será útil en ningún otro lugar). Pero aproximadamente la mitad de las veces debería haberse convertido en una clase exterior.

Thilo agregó una buena respuesta para su primera pregunta “¿Cómo es posible?”. Deseo profundizar un poco en la segunda pregunta: ¿Por qué se permite este comportamiento?

Para empezar, vamos a dejar en claro que este comportamiento no solo está permitido para las clases internas que, por definición, son tipos nesteds no estáticos. Este comportamiento está permitido para todos los tipos nesteds, incluidas las enumeraciones y las interfaces anidadas que deben ser estáticas y no pueden tener una instancia adjunta. Básicamente, el modelo es una simplificación hasta la siguiente statement: el código nested tiene acceso completo al código adjunto, y viceversa.

Entonces, ¿por qué? Creo que un ejemplo ilustra mejor el punto.

Piensa en tu cuerpo y tu cerebro. Si inyectas heroína en tu arm, tu cerebro se drogará. Si la región de la amígdala de su cerebro ve lo que él cree que es una amenaza para su seguridad personal, por ejemplo una avispa, hará que su cuerpo gire al revés y corra hacia las colinas sin que usted “piense” dos veces al respecto.

Entonces, el cerebro es una parte intrínseca del cuerpo y, curiosamente, al revés también. El uso del control de acceso entre entidades tan estrechamente relacionadas pierde su derecho a la relación. Si necesita control de acceso, entonces necesita separar las clases más en unidades verdaderamente distintas. Hasta entonces, son la misma unidad. Un ejemplo de conducción para futuros estudios sería observar cómo se implementa un Java Iterator .

El acceso ilimitado desde el código adjunto al código nested lo hace, en su mayor parte, bastante inútil para agregar modificadores de acceso a campos y métodos de un tipo nested. Hacerlo es agregar desorden y podría proporcionar una falsa sensación de seguridad para los recién llegados al lenguaje de progtwigción Java.

La clase interna se considera como un atributo de la clase Outer. Por lo tanto, no importa si la variable de instancia de clase interna es privada o no, la clase externa puede acceder sin ningún problema, al igual que acceder a sus otros atributos privados (variables).

 class Outer{ private int a; class Inner{ private int b=0; } void outMethod(){ a = new Inner().b; } } 

Debido a que su método main() está en la clase ABC , que puede acceder a su propia clase interna.