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

177

Observé que las clases externas pueden acceder a las variables de instancia privada 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?

Harish
fuente
Esta pregunta me confundió durante bastante tiempo hasta que vi el comentario ... que explica por qué no puedo acceder a xx.x en mi máquina ..
Wang Sheng
44
Los comentarios me confundieron, ejecuto el código anterior en Java 8, se compila y ejecuta. Se puede acceder a xx.x.
Leon
Harish, ¿puedes por favor no aceptar la respuesta aceptada (que no responde la pregunta que hiciste) y en su lugar aceptar la respuesta de Martin Andersson a continuación, que la responde a fondo?
temporary_user_name
FWIW esta sintaxis es absolutamente horrible:new ABC().new XYZ()
Josh M.

Respuestas:

80

La clase interna es solo una forma de separar limpiamente alguna funcionalidad que realmente pertenece a la clase externa original. Están diseñados para usarse 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 relacionada con la forma en que funciona la clase externa.

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

Kaleb Brasee
fuente
217
Esta respuesta explica por qué las clases anidadas tienen acceso a miembros privados de su clase externa. Pero la pregunta es por qué la clase externa tiene acceso a miembros privados de clases anidadas.
Andrew
13
Simplemente agregue "y viceversa" a estos requisitos, las clases internas tienen acceso completo a su clase externa y ahora responde la pregunta.
anthropomo
13
Esta no es la respuesta correcta a este problema, aquí lo hace: stackoverflow.com/questions/19747812/…
Colin Su
44
@anthropomo: No, no lo hace. Ambos requisitos son perfectamente factibles sin que la clase externa tenga acceso a miembros privados de las clases internas.
O Mapper
Un buen ejemplo donde esto es particularmente útil es el Patrón del generador , stackoverflow.com/a/1953567/1262000 . La clase padre solo necesita un constructor que tome un generador y acceda a todas sus variables miembro. De lo contrario, necesitaría tener un constructor privado en la clase padre con todas las variables miembro privadas.
vikky.rk
62

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 a continuación:

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
    }
}
Ich
fuente
Este es un fragmento brillante de código. Justo lo que necesitaba. ¡Gracias!
kevinarpe
Proporcione el código que podría ejecutarse. Lo que es más, por favor, la razón por la cual no se puede permitir el acceso a la variable privada
androidyue
77
Pero entonces ... la clase interna es anónima. No puede crear varias instancias de esa clase interna, ni usar esa clase interna para ninguna declaración de variables, etc.
O Mapper
@OR Mapper es por eso que incluso si xes público aquí, no está permitido como mMember.x.
MAC
54

La clase interna se considera (para fines de control de acceso) como parte de la clase que la contiene. Esto significa acceso completo a todos los privados.

La forma en que esto se implementa es mediante métodos sintéticos protegidos por paquetes: la clase interna se compilará en una clase separada en el mismo paquete (ABC $ XYZ). La JVM no admite este nivel de aislamiento directamente, de modo que en el nivel de código de bytes ABC $ XYZ tendrá métodos protegidos por paquete que la clase externa usa para llegar a los métodos / campos privados.

Thilo
fuente
17

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

Dice que hay una definición de alcance privado en JLS - Determinación de la accesibilidad :

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

Colin Su
fuente
Creo que este fragmento puede responder muy bien a mi pregunta.
shen
5

Un caso de uso importante de la OMI 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 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);
    }
}
thomasfr
fuente
1
Buen comentario pero sin respuesta.
ceving
@cerving Esta es en realidad la única respuesta que me permitió ver un uso real práctico de esta decisión de diseño que de otro modo sería extraña. La pregunta era por qué se decidió de esta manera, y este es un gran razonamiento: una demostración de la diferencia entre lo que puede desear que la clase externa acceda en la clase interna y lo que desea que otras clases no relacionadas accedan.
et_l
3

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 la instancia / clase. Es lógico que las clases internas también tengan acceso ilimitado a los miembros de la clase externa, y que la clase externa tenga acceso ilimitado a los miembros de la clase interna.

Al colocar una clase dentro de otra clase, la está estrechamente vinculada a la implementación, y todo lo que sea parte de la implementación debe tener acceso a las otras partes.

TofuBeer
fuente
3

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 "regulares".

Si, en su caso, no tiene sentido que las clases puedan ver el funcionamiento interno de los demás, lo que básicamente significa que la clase interna simplemente podría haberse convertido en una clase regular, puede declarar la clase interna como static class XYZ. El uso staticsignificará que no compartirán el estado (y, por ejemplo new ABC().new XYZ(), no funcionará, y tendrá que usarlo new ABC.XYZ().
Pero, si ese es el caso, debe pensar si XYZrealmente debería ser una clase interna y si tal vez merece su propia). 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á usando su clase externa, y eso no será útil en ningún otro lado). Pero aproximadamente la mitad del tiempo Debería haberse hecho una clase externa.

abyx
fuente
2
La clase externa también puede acceder a los miembros privados de las clases internas estáticas , por lo que esto no tiene nada que ver con la estática . Usted dice "no tiene sentido que las clases puedan ver el funcionamiento interno de los demás", pero ese no es necesariamente el caso, ¿y si tiene sentido que la clase interna vea el funcionamiento interno de la clase externa, pero no el vicio? viceversa
O Mapper
3

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

Para empezar, seamos perfectamente claros de que este comportamiento no se limita a las clases internas, que por definición son tipos anidados no estáticos. Este comportamiento está permitido para todos los tipos anidados, incluidas las enumeraciones e interfaces anidadas que deben ser estáticas y no pueden tener una instancia de cierre. Básicamente, el modelo es una simplificación de la siguiente declaración: el código anidado tiene acceso completo al código adjunto, y viceversa.

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

Piensa en tu cuerpo y tu cerebro. Si inyectas heroína en tu brazo, tu cerebro se eleva. 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, también al revés. El uso del control de acceso entre esas entidades estrechamente relacionadas pierde su reclamo de 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 Iteratorse implementa generalmente un Java .

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

Martin Andersson
fuente
1
Esta debería ser la respuesta aceptada. Tan claro y minucioso. En cambio, la respuesta aceptada ni siquiera aborda la pregunta.
temporary_user_name
En mi opinión, esto todavía no responde a la pregunta de por qué no podemos agregar fácilmente campos privados a una clase interna, a la que la clase externa no puede acceder directamente. A menos que me equivoque, esto arruina uno de los casos primarios para las clases internas: la creación de tipos inmutables de "estructura" de corta duración. FWIW C # felizmente apoya esto: repl.it/repls/VengefulCheeryInverse
Josh M.
-1

La clase interna se considera como un atributo de la clase externa. Por lo tanto, no importa que la variable de instancia de clase interna sea 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;
}
}
MonMoonkey
fuente
-2

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

aberrante80
fuente
2
La pregunta no es si los miembros de la ABCclase pueden acceder a las clases anidadas en la ABCclase, sino por qué pueden acceder a los miembros privados de las clases anidadas en la ABCclase en Java.
O Mapper
Respondí la pregunta el mismo día en que se hizo. Alguien editó la pregunta 2 años después, y el voto negativo llegó 3 años después. Estoy bastante seguro de que quien editó la pregunta cambió demasiado la redacción de la pregunta.
aberrant80