¿Por qué las clases externas de Java pueden acceder a los miembros privados de las clases internas?


Observé que las clases externas pueden acceder a las clases internas variables de instancia privada. ¿Cómo es esto posible? Aquí hay un código de ejemplo 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?

Author: Paŭlo Ebermann, 2009-11-26

10 answers

La clase interna es solo una forma de separar limpiamente algunas funcionalidades que realmente pertenecen a la clase externa original. Están destinados a ser utilizados cuando se tienen 2 requisitos:

  1. Alguna parte de la 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 estrechamente ligada a 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 métodos y atributos de la clase externa, incluidos los privados.

 78
Author: Kaleb Brasee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-26 05:52:13

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 siguiente:

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
    }
}
 53
Author: Ich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-22 21:09:58

La clase interna se considera (a efectos de control de acceso) parte de la clase contenedora. Esto significa acceso completo a todos los privados.

La forma en que esto se implementa es usando métodos sintéticos protegidos por paquetes: La clase interna se compilará en una clase separada en el mismo paquete (ABC X XYZ). La JVM no admite este nivel de aislamiento directamente, por lo que en el nivel de bytecode ABC have XYZ tendrá métodos protegidos por paquetes que la clase externa usa para llegar al nivel privado métodos/campos.

 48
Author: Thilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-26 06:01:18

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

Dice que hay una definición de ámbito privado en JLS-Determinar la Accesibilidad :

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

 14
Author: Colin Su,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:36

Un caso de uso importante de IMHO para 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 que encierra, como se muestra a continuación. También están funcionando las restricciones de acceso entre clases estáticas en la misma clase que encierra. Yo estaba sorprender ...

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);
    }
}
 4
Author: thomasfr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:36

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

Al poner una clase dentro de otra clase, está haciendo que esté estrechamente vinculada a la implementación, y cualquier cosa que sea parte de la la implementación debe tener acceso a las otras partes.

 3
Author: TofuBeer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-26 06:27:39

La lógica detrás de las clases internas es que si creas una clase interna en una clase externa, eso 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 la otra, 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. Usando static significará que no compartirán el estado (y, por ejemplo, new ABC().new XYZ() no funcionará, y tendrá que usar new ABC.XYZ().
Pero, si ese es el caso, deberías pensar si XYZ debería ser realmente una clase interna y que 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 su clase externa esté usando, y eso no será útil en ningún otro lugar). Pero alrededor de la mitad de las veces se debería haber hecho un clase externa.

 3
Author: abyx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-26 06:42:18

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

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

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

Piensa en tu cuerpo y tu cerebro. Si te inyectas heroína en el brazo, tu cerebro se pone alto. 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 correrá hacia las colinas sin Lo" piensas " dos veces.

Por lo tanto, el cerebro es una parte intrínseca del cuerpo, y curiosamente, también al revés. El uso de control de acceso entre entidades estrechamente relacionadas pierde su reclamación de relación. Si necesita control de acceso, entonces necesita separar más las clases en unidades verdaderamente distintas. Hasta entonces, son la misma unidad. Un ejemplo de conducción para estudios adicionales sería ver cómo un Java Iterator por lo general es aplicado.

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

 1
Author: Martin Andersson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:46

La clase interna es considerada como un atributo de la clase Externa. Por lo tanto, no importa que la variable de instancia de la 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;
}
}
 -1
Author: MonMoonkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-25 03:13:13

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

 -2
Author: aberrant80,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-26 05:49:26