ventajas usar generar español ejemplo documentacion como comentarios comando codigo all java visibility inner-classes

usar - javadoc wiki



¿Por qué pueden las clases secundarias anidadas acceder a miembros privados de su clase principal, pero los nietos no pueden? (3)

Probablemente similar a la pregunta, ¿Por qué pueden las clases Java externas acceder a miembros privados de clase interna? o Acceda a los campos privados de superclase utilizando la palabra clave super en una subclase .

Pero hay algunas diferencias: la clase de niños puede acceder a los miembros privados de su clase de padres (y solo al padre más cercano ).

Dado el código de muestra a continuación:

public class T { private int t; class T1 { private int t1; public void test() { System.out.println(t); } } class T2 extends T1 { private int t2; public void test() { System.out.println(t); System.out.println(super.t1); System.out.println(this.t2); } } class T3 extends T2 { public void test() { System.out.println(t); System.out.println(super.t1); // NG: t1 Compile error! Why? System.out.println(super.t2); // OK: t2 OK } } }


Métodos de acceso sintético

Técnicamente, en el nivel de JVM , NO se puede acceder a ningún miembro private de otra clase, ni a los de una clase T2.t2 ( Tt ), ni a los de una clase primaria ( T2.t2 ). En su código, parece que puede hacerlo, porque el compilador genera métodos de acceso synthetic para usted en las clases a las que accedió. Lo mismo sucede cuando en la clase T3 arreglas la referencia no válida super.t1 con la forma correcta ((T1) this).t1 .

Con la ayuda de tal método de acceso synthetic generado por el compilador, en general puede acceder a cualquier miembro private de cualquier clase anidada en la clase T externa (nivel superior), por ejemplo, desde T1 puede usar la new T2().t2 . Tenga en cuenta que esto también se aplica a private static miembros private static .

El atributo synthetic se introdujo en la versión 1.1 de JDK para admitir clases anidadas, una nueva característica de lenguaje en java en ese momento. Desde entonces, el JLS permite explícitamente el acceso mutuo a todos los miembros dentro de una clase de nivel superior, incluidos los private .

Pero para la compatibilidad con versiones anteriores, el compilador deshace las clases anidadas (por ejemplo, a T$T1 , T$T2 , T$T3 ) y traduce private accesos de los miembros private a las llamadas a métodos de acceso synthetic generados (por lo tanto, estos métodos deben tener el paquete privado , es decir, predeterminado). , visibilidad):

class T { private int t; T() { // generated super(); // new Object() } static synthetic int access$t(T t) { // generated return t.t; } } class T$T1 { private int t1; final synthetic T t; // generated T$T1(T t) { // generated this.t = t; super(); // new Object() } static synthetic int access$t1(T$T1 t$t1) { // generated return t$t1.t1; } } class T$T2 extends T$T1 { private int t2; { System.out.println(T.access$t((T) this.t)); // t System.out.println(T$T1.access$t1((T$T1) this)); // super.t1 System.out.println(this.t2); } final synthetic T t; // generated T$T2(T t) { // generated this.t = t; super(this.t); // new T1(t) } static synthetic int access$t2(T$T2 t$t2) { // generated return t$t2.t2; } } class T$T3 extends T$T2 { { System.out.println(T.access$t((T) this.t)); // t System.out.println(T$T1.access$t1((T$T1) this)); // ((T1) this).t1 System.out.println(T$T2.access$t2((T$T2) this)); // super.t2 } final synthetic T t; // generated T$T3(T t) { // generated this.t = t; super(this.t); // new T2(t) } }

NB: no se le permite referirse a miembros synthetic directamente, por lo que en el código fuente no puede usar, por ejemplo, int i = T.access$t(new T()); tú mismo.


¡Ejemplo inteligente! Pero en realidad es una explicación un tanto aburrida: no hay problema de visibilidad, simplemente no hay manera de referirse a t1 directamente desde T3 porque super.super no está permitido .

T2 no puede acceder a su propio campo t1 directamente ya que es privado (y las clases secundarias no heredan los campos privados de sus padres), pero super es efectivamente una instancia de T1 y, dado que está en la misma clase, T2 puede referirse a los campos privados de super Simplemente, no hay ningún mecanismo para que T3 dirija directamente a los campos privados de su clase de abuelos T1 .

Ambos compilan bien dentro de T3 , lo que demuestra que un T3 puede acceder a private campos private sus abuelos:

System.out.println(((T1)this).t1); System.out.println(new T1().t1);

A la inversa, esto no se compila ni en T2 ni en T3 :

System.out.println(t1);

Si se permitiera super.super , podrías hacer esto desde T3 :

System.out.println(super.super.t1);

si definiera 3 clases, A , B , C , A con un campo protegido t1 y B heredaría de A y C de B , C podría referirse a A s t1 invocando super.t1 porque es visible aquí. lógicamente, ¿no debería aplicarse lo mismo a la herencia de clases internas incluso si el campo es privado, porque estos miembros privados deberían ser visibles debido a que están en la misma clase?

(Voy a seguir con los nombres de las clases T1 , T2 y T3 OP por simplicidad)

Si t1 estuviera protected no habría ningún problema: T3 podría referirse al campo t1 directamente como cualquier subclase. El problema se presenta con private porque una clase no tiene conocimiento de private campos private de sus clases principales y, por lo tanto, no puede hacer referencia a ellos directamente, aunque en la práctica son visibles. Es por eso que tiene que usar super.t1 de T2 , incluso para referirse al campo en cuestión.

Aunque en lo que respecta a un T3 no tiene un campo t1 , tiene acceso a private campos private T1 al estar en la misma clase externa. Como ese es el caso, todo lo que necesita hacer es enviar this a un T1 y tiene una forma de referirse al campo privado. La llamada super.t1 en T2 está (en esencia) enviando this a un T1 que nos permite referirnos a sus campos.


Muy buen hallazgo! Creo que, todos habíamos asumido que su ejemplo de código debería compilar.

Desafortunadamente, no es el caso ... y el JLS nos da una respuesta en §15.11.2. "Accediendo a los miembros de Superclass usando super" (énfasis mío):

Supongamos que una expresión de acceso de campo super.f aparece dentro de la clase C, y la superclase inmediata de C es la clase S. Si se puede acceder a f en S desde la clase C (§6.6), entonces la superf se trata como si hubiera sido el expresión this.f en el cuerpo de la clase S. De lo contrario, se produce un error en tiempo de compilación.

Se da accesibilidad porque todos los campos están en la misma clase adjunta. Pueden ser privadas pero siguen siendo accesibles.

El problema es que en T2 (la superclase inmediata de T3 ) el tratamiento de super.t1 ya que this.t1 es ilegal, no hay campo t1 en T2 . De ahí el error del compilador.