En Java, acabo de descubrir que el siguiente código es legal:
KnockKnockServer newServer = new KnockKnockServer();
KnockKnockServer.receiver receive = newServer.new receiver(clientSocket);
FYI, el receptor es solo una clase auxiliar con la siguiente firma:
public class receiver extends Thread { /* code_inside */ }
Nunca antes había visto la XYZ.newnotación. ¿Cómo funciona? ¿Hay alguna forma de codificar eso de manera más convencional?

newera un operador en muchos idiomas. (¿Pensé que también podrías sobrecargarnewen C ++?) Sin embargo, la clase interna de Java es un poco extraña para mí.Respuestas:
Es la forma de crear una instancia de una clase interna no estática desde fuera del cuerpo de la clase contenedora, como se describe en los documentos de Oracle .
Cada instancia de clase interna está asociada con una instancia de su clase contenedora. Cuando tiene
newuna clase interna desde dentro de su clase contenedora, usa lathisinstancia del contenedor de forma predeterminada:Pero si desea crear una instancia de Bar fuera de Foo, o asociar una nueva instancia con una instancia contenedora que no sea
thisentonces, debe usar la notación de prefijo.fuente
f.new.publicel nivel de acceso en , sería imposible crear una instancia de esta manera, ¿verdad? Para extender el comentario de @EricJablow, las clases internas generalmente siempre deben tener un nivel de acceso predeterminado .KnockKnockServer.receiverprivateprivatereceiverclase desde fuera. Si lo estuviera diseñando, probablemente tendría la clase public pero su constructor protegido o package-private, y tendría un métodoKnockKnockServerpara crear instancias de receptor.x.new.Eche un vistazo a este ejemplo:
Usando javap podemos ver las instrucciones generadas para este código
Método principal:
Constructor de clases internas:
Todo es simple: al invocar el constructor TestInner, java pasa la instancia de prueba como primer argumento principal: 12 . Sin mirar ese TestInner debería tener un constructor sin argumentos. TestInner a su vez solo guarda la referencia al objeto principal, Test $ TestInner: 2 . Cuando invoca el constructor de clase interna desde un método de instancia, la referencia al objeto principal se pasa automáticamente, por lo que no tiene que especificarla. En realidad, pasa cada vez, pero cuando se invoca desde fuera debe pasarse explícitamente.
t.new TestInner();- es solo una forma de especificar el primer argumento oculto para el constructor TestInner, no un tipométodo () es igual a:
TestInner es igual a:
fuente
Cuando se agregaron clases internas a Java en la versión 1.1 del lenguaje, originalmente se definieron como una transformación a un código compatible con 1.0. Si miras un ejemplo de esta transformación, creo que aclarará mucho cómo funciona realmente una clase interna.
Considere el código de la respuesta de Ian Roberts:
Cuando se transforma a un código compatible con 1.0, esa clase interna
Barse convertiría en algo como esto:El nombre de la clase interna tiene como prefijo el nombre de la clase externa para que sea único. Se
this$0agrega un miembro privado oculto que contiene una copia del externothis. Y se crea un constructor oculto para inicializar ese miembro.Y si miras el
createBarmétodo, se transformaría en algo como esto:Entonces, veamos qué sucede cuando ejecuta el siguiente código.
Primero instanciamos una instancia de
Fooe inicializamos elvalmiembro en 5 (es decirf.val = 5).A continuación, llamamos
f.createBar(), que instancia una instancia deFoo$Bare inicializa elthis$0miembro con el valor dethispasado decreateBar(es decirb.this$0 = f).Finalmente llamamos a
b.printVal()cuál intenta imprimirb.this$0.valcuál esf.valcuál es 5.Ahora que era una instanciación regular de una clase interna. Veamos qué sucede cuando se crea una instancia
Bardesde el exteriorFoo.Aplicando nuestra transformación 1.0 nuevamente, esa segunda línea se convertiría en algo como esto:
Esto es casi idéntico a la
f.createBar()llamada. Nuevamente, estamos creando una instancia deFoo$Bare inicializando elthis$0miembro en f. Así que de nuevob.this$0 = f.Y nuevamente cuando llamas
b.printVal(), estás imprimiendob.thi$0.valcuál esf.valcuál es 5.La clave para recordar es que la clase interna tiene un miembro oculto que tiene una copia de
thisla clase externa. Cuando crea una instancia de una clase interna desde dentro de la clase externa, se inicializa implícitamente con el valor actual dethis. Cuando crea una instancia de la clase interna desde fuera de la clase externa, especifica explícitamente qué instancia de la clase externa usar, a través del prefijo de lanewpalabra clave.fuente
Piense
new receiveren una sola ficha. Algo así como el nombre de una función con un espacio en él.Por supuesto, la clase
KnockKnockServerno tiene literalmente una función nombradanew receiver, pero supongo que la sintaxis debe sugerir eso. Está destinado a que parezca que estás llamando a una función que crea una nueva instancia deKnockKnockServer.receiveruso de una instancia particular deKnockKnockServerpara cualquier acceso a la clase adjunta.fuente
new receiveren una sola ficha. ¡muchas gracias!Sombreado
Si una declaración de un tipo (como una variable miembro o un nombre de parámetro) en un ámbito particular (como una clase interna o una definición de método) tiene el mismo nombre que otra declaración en el ámbito adjunto, entonces la declaración ensombrece la declaración del alcance adjunto. No puede referirse a una declaración sombreada solo por su nombre. El siguiente ejemplo, ShadowTest, demuestra esto:
El siguiente es el resultado de este ejemplo:
Este ejemplo define tres variables llamadas x: la variable miembro de la clase ShadowTest, la variable miembro de la clase interna FirstLevel y el parámetro en el método methodInFirstLevel. La variable x definida como parámetro del método methodInFirstLevel sombrea la variable de la clase interna FirstLevel. En consecuencia, cuando usa la variable x en el método methodInFirstLevel, se refiere al parámetro del método. Para hacer referencia a la variable miembro de la clase interna FirstLevel, use la palabra clave this para representar el ámbito adjunto:
Consulte las variables miembro que encierran ámbitos más grandes por el nombre de clase a la que pertenecen. Por ejemplo, la siguiente instrucción accede a la variable miembro de la clase ShadowTest desde el método methodInFirstLevel:
Consulte los documentos
fuente