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 campoval
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.