una que privada otra objeto nombre llevan internas estaticas dentro crear clases clase anonimas java inheritance inner-classes

java - que - crear un objeto de una clase dentro de otra clase



Comportamiento de miembros diferentes de la clase interna si la clase interna se extiende a la clase exterior? (5)

Como InheritedB extends B , la creación de una instancia de InheritedB le otorga un atributo val , que es "antiguo" por defecto para cualquier nueva clase B o instancia de subclase.

Aquí, InheritedB imprime su propio atributo val , no el de la instancia B adjunta.

Hoy tropecé con algún extraño comportamiento de clase interno (no estático).

Si tengo las siguientes clases ...

class B { String val = "old"; void run(){ val = "new"; System.out.println(val); // outputs: new new InnerB().printVal(); // outputs: new } private class InnerB { void printVal(){ System.out.println(val); } } } new B().run();

... todo parece estar claro. La instancia de InnerB pertenece a la instancia de B, por lo que si arroja val, imprime el valor ya reemplazado ''nuevo''.

PERO si la clase interna extiende la clase externa, esto no funciona.

class B { String val = "old"; void run(){ val = "new"; System.out.println(val); // outputs: new new InnerB().printVal(); // outputs: new new InheritedB().printVal(); // outputs: old new } private class InnerB { void printVal(){ System.out.println(val); } } private class InheritedB extends B{ void printVal(){ System.out.println(val + " "+ B.this.val); } } } new B().run(); // outputs: new new old!

Si echo un vistazo a los constructores, también veo que se creará una nueva instancia de B si se crea una instancia de InheritedB.

Encuentro esto muy extraño ... ¿Alguien puede explicar por qué hay esta diferencia?


En el caso de InheritedB hay dos variables llamadas val , la de B y la de InheritedB . La aplicación de reglas de visibilidad da el resultado observado.


Esta línea:

new InheritedB().printVal();

crea una nueva instancia de InheritedB , cuya instancia contenedora es la instancia existente de B (donde val es "new" ). Pero en este punto hay dos variables val :

  • El que está en la instancia existente de B
  • El que está en la instancia de InheritedB , que tiene un campo val separado

El valor de la segunda variable es "old" porque efectivamente es el valor predeterminado del campo.

Esta declaración en InheritedB :

System.out.println(val + " "+ B.this.val);

imprime el valor de val heredado de B , seguido por el valor de val en la "instancia que contiene".

Podría ser más simple pensar que se está refactorizando para:

public class B { String val = "old"; } public class InheritedB extends B { B other; public InheritedB(B other) { this.other = other; } void printVal() { System.out.println(val + " "+ other.val); } }

Entonces básicamente estás ejecutando:

B original = new B(); original.val = "new": InheritedB inherited = new InheritedB(original); inherited.printVal();

Con suerte, puedes seguir exactamente lo que está pasando allí. El compilador está ejecutando aproximadamente su código original en ese código.


La diferencia es la clase InnerB no tiene miembro val en ella. donde como clase InheritedB extiende clase B y tiene su propia copia del miembro val .

void run(){ val = "new"; //<--- modifies B''s val not InheritedB''s val System.out.println(val); // outputs: new new InnerB().printVal(); // outputs: new new InheritedB().printVal(); // outputs: old new }

En el bloque de código anterior, printVal de InnerB accede al miembro de val del contenedor, cuyo valor ya se modificó en el método de run para valorar el valor nuevo .

Pero la copia de val en el objeto de InheritedB sigue siendo un valor " antiguo ", no modificado, y la función printVal usa ese valor.


val en InheritedB refiere al val de su clase base ( super.val ), ya que es parte de this .

Si no hereda de la clase externa, val refiere al alcance de la clase externa ( B.this.scope ). Sin embargo, dado que heredas, tiene un alcance más cercano y, por lo tanto, oculta el alcance externo.

Como nunca this.val run() en el interior de this , this.val sigue siendo old .

Si echo un vistazo a los constructores, también veo que se creará una nueva instancia de B si se crea una instancia de InheritedB.

Sí; crear una clase derivada siempre creará una instancia de su clase base. No hay forma de heredar de una instancia existente.