java - tipos - que clases internas no llevan nombre
Java: constructores sintetizados de clase interna privada (3)
Esta no es una respuesta, que creo que ha sido bien cubierta por las sbridges . Es simplemente un ejemplo de trabajo que produce el comportamiento que describe:
public class Outer {
private class Inner {
}
public static void main(String[] args) {
printConstructors();
//only one constructor is printed but two would appear if you
//uncommented the line below
//new Outer().new Inner();
}
private static void printConstructors() {
Constructor[] constructors = Outer.Inner.class.getDeclaredConstructors();
for (Constructor c : constructors) {
System.out.println(c.toGenericString());
}
}
}
Esta pregunta ya tiene una respuesta aquí:
Tengo una clase Outer
que tiene una clase private Inner
.
En mi método de clase Outer
, hago una instancia de la clase Inner
la siguiente manera:
Outer outer = new Outer();
Inner inner = outer.new Inner();
El compilador convierte este código a:
Outer outer = new Outer();
Inner inner = new Inner(outer, null);
El uso de la reflexión muestra que la clase Inner
tiene los siguientes constructores sintetizados:
private Outer$Inner(Outer)
Outer$Inner(Outer,Outer$Inner)
Como la clase Inner
es private
, el compilador le agrega ese constructor private
para que nadie pueda instanciar esa clase. Pero, obviamente, la clase Outer
debería poder crear instancias, por lo que el compilador agrega ese otro constructor privado de paquete que a su vez llama al constructor privado. Además, dado que el constructor de paquetes privados tiene ese $
en su nombre, el código Java normal no puede llamarlo.
Pregunta: ¿Por qué sintetizar un constructor privado y un paquete privado? ¿Por qué no sintetizar solo el constructor de paquetes privados y terminar con él?
La respuesta más probable es respetar lo que declaraste en tu código fuente. Hacer esto aún permite usar el constructor privado por reflexión como lo declaraste.
Esto también evita verificar si al constructor privado se llama realmente dentro de la clase Inner
.
Si escribes el código como,
public class Outer {
private class Inner {}
}
Notarás que solo hay un constructor private Outer$Inner(Outer)
Este constructor es requerido por la Sección 8.8.9 de JLS , que dice que si no se define ningún constructor, se debe generar un constructor predeterminado, y en este caso el constructor predeterminado debe ser privado,
En un tipo de clase, si la clase se declara pública, al constructor predeterminado se le otorga implícitamente el modificador de acceso público (§6.6); si la clase se declara protegida, entonces el constructor predeterminado recibe implícitamente el modificador de acceso protegido (§6.6); si la clase se declara privada, entonces al constructor predeterminado se le asigna implícitamente el modificador de acceso privado (§6.6); de lo contrario, el constructor predeterminado tiene el acceso predeterminado implícito por ningún modificador de acceso.
Sin embargo, cuando creas una instancia de Inner inside Outer con un código como,
public class Outer {
private class Inner {}
public String foo() {
return new Inner().toString();
}
}
El compilador debe generar un constructor al que Outer puede llamar legalmente (no se puede llamar legalmente al constructor predeterminado privado porque es privado). Por lo tanto, el compilador debe generar un nuevo constructor sintético. El nuevo constructor debe ser sintético, según la sección 13.1 de la JLS.
Cualquier construcción introducida por el compilador que no tenga una construcción correspondiente en el código fuente debe marcarse como sintética, a excepción de los constructores predeterminados y el método de inicialización de clase.
Este segundo constructor no tiene una construcción correspondiente en el código fuente, por lo que este nuevo constructor debe ser sintético. El primer constructor privado aún debe generarse, ya que JLS requiere un constructor predeterminado privado.