2009-07-06 16 views
10

Considere el siguiente caso:Java clase interna rompecabezas visibilidad

public class A { 
    public A() { b = new B(); } 
    B b; 
    private class B { } 
} 

De una advertencia en Eclipse que cito: el compilador Java emula la A.B constructor() por un método de acceso sintético. Supongo que el compilador ahora sigue adelante y crea un constructor extra "bajo el agua" para B.

Creo que esto es bastante extraño: ¿por qué la clase B no sería visible como a.k.o. campo en A? Y, ¿significa que la clase B ya no es privada en tiempo de ejecución? Y: ¿por qué se comporta la palabra clave protegida para la clase B diferente?

public class A { 
    public A() { b = new B(); } 
    B b; 
    protected class B { } 
} 

Respuesta

24

Las clases internas son esencialmente un corte introducido en Java 1.1. La JVM en realidad no tiene ningún concepto de una clase interna, por lo que el compilador debe manipularla. El compilador genera la clase B "fuera" de la clase A, pero en el mismo paquete, y luego le agrega acceso/constructores sintéticos para permitir que A tenga acceso a ella.

Cuando le das a B un constructor protegido, A puede acceder a ese constructor ya que está en el mismo paquete, sin necesidad de agregar un constructor sintético.

+0

OK, lo veo. Para mí esto significa que evitaré el uso de clases internas como en el ejemplo, solo puede generar confusión. – Gerard

+2

No dejaría que te moleste. Esa advertencia particular del compilador no es muy útil para nadie, los métodos sintéticos se usan todo el tiempo con las clases internas y no tienen un impacto significativo. – skaffman

+0

En mi humilde opinión, los métodos sintéticos eran una adición innecesaria al idioma. El solo uso del alcance del paquete para los miembros "privados" (que el compilador hace de todos modos) fue una solución satisfactoria. – finnw

-1

Es necesario utilizar

this.new B(); 
+0

lo siento, pero this.new B(); da la misma advertencia y comportamiento. – Gerard

+2

@Rats: eso no hará ninguna diferencia al problema en la mano. La calificación "this" es implícita. – skaffman

2

El acceso de class B y su constructor no tiene que ser el mismo. Puede tener una clase interna privada con un constructor de alcance de paquete, y esto es lo que suelo hacer.

public class A { 
    public A() { b = new B(); } 
    B b; 
    private class B { 
    B() { } 
    } 
} 
+0

Pero, ¿cómo se puede tener una instancia privada del paquete de una clase privada? – einpoklum

4

Sé que esta pregunta es ahora casi tres años, pero me parece que una parte de la pregunta sigue sin respuesta:

Y: ¿significa que la clase B ya no es privada ¿en tiempo de ejecución?

comentario Carlos Heubergers en skaffmans respuesta sugiere, clase B sigue siendo private para otras clases del paquete.

Probablemente sea adecuado para el lenguaje de programación Java, es decir, no es posible hacer referencia a la clase B de otra clase. Al menos no sin usar el reflejo (con el cual también se puede acceder a miembros privados de la clase desde el exterior), pero este es otro problema.

Pero como la JVM no tiene ningún concepto de una clase interna (como dice skaffman), me pregunté cómo se logra una visibilidad "accesible por una sola clase" en el nivel de bytecode. La respuesta: no se realiza en absoluto, para la JVM la clase interna parece una clase privada de paquete normal. Esto es, si escribe bytecode para usted (o modifica uno generado por el compilador) puede acceder a la clase B sin problemas.

Puede acceder a todos los métodos de acceso sintético de todas las clases en el mismo paquete, también. Por lo tanto, si asigna un valor a un campo privado de la clase A en un método de la clase B, se genera un método de acceso sintético con visibilidad predeterminada (es decir, paquete privado) en la clase A (llamado algo como access$000) que establece el valor para usted. Se supone que este método solo debe invocarse desde la clase B (y, de hecho, solo se puede invocar desde allí utilizando el lenguaje Java).Pero desde el punto de vista de JVM, este es solo un método como cualquier otro y puede ser llamado por cualquier clase.

lo tanto, para responder a la pregunta:

  • Desde el punto de vista de idiomas de Java, la clase es B y se mantiene privado.
  • Desde el punto de vista de las JVM, la clase B (o mejor: clase A$B) no es privada.
+0

correcto, pero eso no es lo que sugerí. Escribí "los ** miembros ** son aún privados" - Me refiero a los campos de la clase y no a la ** clase ** según lo interpretado por ti. También esta pregunta es sobre [tag: java] y no [tag: bytecode] (generación, modificación, _hacking_ ...). –

+0

@CarlosHeuberger por supuesto esto no fue una ofensa para usted! Ciertamente, los miembros de la clase B siguen siendo privados, pero la clase B (como una especie de miembro de la clase A) no lo es (desde el punto de vista de las JVM), y esta fue la pregunta original de Gerard. Y sí, la pregunta es sobre Java, pero como se indica en la etiqueta wiki: "Java es un lenguaje de programación y ** entorno de ejecución **". Y el entorno de tiempo de ejecución es la JVM y la JVM per se no tiene nada que ver con el lenguaje de programación Java, sino que solo interpreta bytecode. – siegi