Today I stumbled about some strange inner (non-static) class behaviour.
If I have the following classes ...
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();
... everything seems to be clear. The instance of InnerB belongs to the instance of B so if it should output val it prints the already replaced value 'new'.
BUT if the inner class extends the outer class this doesn't work.
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!
If I have a look at the constructors I also see that a new instance of B will be created if instance of InheritedB is created.
I find this very strange... Can somebody explain why there is this difference?
This line:
new InheritedB().printVal();
creates a new instance of InheritedB
, whose containing instance is the existing instance of B
(where val is "new"
). But at this point there are two val
variables:
B
InheritedB
, which has a separate val
fieldThe value of the second variable is "old"
because that's effectively the default value of the field.
This statement in InheritedB
:
System.out.println(val + " "+ B.this.val);
prints out the value of val
inherited from B
, followed by the value of val
in the "containing instance".
It might be simpler to think of it being refactored to:
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);
}
}
Then you're basically running:
B original = new B();
original.val = "new":
InheritedB inherited = new InheritedB(original);
inherited.printVal();
Hopefully you can follow exactly what's going on there. The compiler is roughly performing your original code into that code.
See more on this question at Stackoverflow