2009-03-19 24 views
26

No entiendo por qué esta compila. f() y g() son visibles desde las clases internas, a pesar de ser privadas. ¿Se los trata especialmente porque son clases internas?¿Por qué las clases internas hacen accesibles los métodos privados?

Si A y B no son clases estáticas, siguen siendo las mismas.

class NotPrivate { 
    private static class A { 
     private void f() { 
      new B().g(); 
     } 
    } 

    private static class B { 
     private void g() { 
      new A().f(); 
     } 
    } 
} 

Respuesta

26

(Edit: ampliado de la respuesta a responder algunas de las observaciones)

El compilador toma las clases interiores y los convierte en clases de nivel superior. Como los métodos privados solo están disponibles para la clase interna, el compilador tiene que agregar nuevos métodos "sintéticos" que tengan acceso a nivel de paquete para que las clases de nivel superior tengan acceso a él.

Algo como esto (los $ aplicaciones que se agregan por el compilador):

class A 
{ 
    private void f() 
    { 
     final B b; 

     b = new B(); 

     // call changed by the compiler 
     b.$g(); 
    } 

    // method generated by the compiler - visible by classes in the same package 
    void $f() 
    { 
     f(); 
    } 
} 

class B 
{ 
    private void g() 
    { 
     final A a; 

     a = new A(); 

     // call changed by the compiler 
     a.$f(); 
    } 

    // method generated by the compiler - visible by classes in the same package 
    void $g() 
    { 
     g(); 
    } 
} 

clases no estáticos son los mismos, pero tienen la adición de una referencia a la clase exterior, de manera que los métodos pueden ser llamado sobre eso.

La razón por la que Java lo hace de esta manera es que no querían requerir cambios de máquina virtual para admitir clases internas, por lo que todos los cambios tenían que ser al nivel del compilador.

El compilador toma la clase interna y la convierte en una clase de nivel superior (por lo tanto, en el nivel VM no existe una clase interna). El compilador también debe generar los nuevos métodos de "reenvío". Están hechos a nivel de paquete (no público) para garantizar que solo las clases en el mismo paquete puedan acceder a ellos. El compilador también actualizó las llamadas de método a los métodos privados a los métodos de "reenvío" generados.

Puede evitar que el compilador genere el método al declarar los métodos como "paquete" (la ausencia de protección pública, privada y protegida). La desventaja de eso es que cualquier clase en el paquete puede llamar a los métodos.

+1

entonces, declaras un método privado y el compilador lo convierte en público en lugar de generar un error? – cbrulak

+0

bien, no es público, es un paquete. Y, sí, eso es lo que hace. Esto se debe a que la VM no tiene conocimiento de las clases internas ... por lo que el compilador hace todo el trabajo en lugar de hacer que la VM haga cosas especiales. – TofuBeer

+0

paquete que es la clase que contiene la clase interna? – cbrulak

3

Java compila en accesadores especiales con $ en ellos. Entonces no puedes escribir Java que acceda a los métodos privados. Se explica aquí:

http://www.retrologic.com/innerclasses.doc7.html

No es una categoría más de los miembros generados por el compilador. Un miembro privado m de una clase C puede ser utilizado por otra clase D, si una clase encierra la otra, o si están encerradas por una clase común. Como la máquina virtual no conoce este tipo de agrupación, el compilador crea un protocolo local de métodos de acceso en C para permitir que D lea, escriba o llame al miembro m. Estos métodos tienen nombres del formulario acceso $ 0, acceso $ 1, etc. Nunca son públicos. Los métodos de acceso son únicos ya que pueden agregarse a las clases adjuntas, no solo a las clases internas.

13

creo this quote lo resume muy bien:

... clases internas pueden acceder a todos los miembros de la clase que se declara, incluso los miembros privados. De hecho, se dice que la clase interna es un miembro de la clase; por lo tanto, siguiendo las reglas de la ingeniería orientada a objetos, debe tener acceso a todos los miembros de la clase.

Y a raíz de eso, dado que ambas clases internas son realmente solo parte de la clase contenedora, también deberían poder acceder a las demás miembros privados.

+0

"De hecho, cuando se compila tu clase, todas las clases internas están 'colapsadas' para formar parte de la clase contenedora". -- ¿Qué quiere decir con esto? –

+0

Estoy bastante seguro de que no es verdad. Por lo general, se crean clases de la forma Exterior $ Inner. Realmente no puedes hacer lo que dijiste porque puedes tener diferentes números de objetos de la clase interna y externa. –

+0

Tienes razón, eso fue un poco engañoso. Eliminé esa parte de mi respuesta. –

0

Se requiere que funcione según la especificación, es decir. el lenguaje Java Spec dice así:

6.6.1 Determinación de Accesibilidad (por lo menos desde JLS6)

"De lo contrario, si el miembro o constructor se declara privada, entonces se permite el acceso si y sólo si se produce dentro del cuerpo de la clase de nivel superior (§7.6) que incluye la declaración del miembro o constructor ".

I.e. el "acceso-alcance" de un miembro privado es: en todas partes dentro de los límites léxicos del cuerpo de clase de nivel superior.

Eso significa que se puede acceder a todos los miembros privados que se definen dentro del cuerpo de clase de la clase más externa desde cualquier otro lugar en este cuerpo de clase.

Por ejemplo, se puede acceder al método privado de una clase interna desde los métodos de la clase externa o desde cualquier método de otra clase interna de la clase externa.

Cuestiones relacionadas