Reputation: 27203
Consider the following Test
class to demonstrate the inner class behavior in Java. The main code is in the run
method. Rest is just plumbing code.
public class Test {
private static Test instance = null;
private Test() {
}
private void run() {
new Sub().foo();
}
public static void main(String[] args) {
instance = new Test();
instance.run();
}
class Super {
protected void foo() {
System.out.println("Test$Super.Foo");
}
}
class Sub extends Super {
public void foo() {
System.out.println("Test$Sub.Foo");
super.foo();
}
}
}
I am just printing below the javap output for the hidden Sub
constructor:
so.Test$Sub(so.Test);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:Lso/Test;
5: aload_0
6: aload_1
7: invokespecial #2 // Method so/Test$Super."<init>":(Lso/Test;)V
10: return
Normally the compiler ensures that the sub-class constructor would call into the super-class constructor first before going on to initialize its own fields. This aids in a correctly constructed object but I am seeing an aberration from the normative behavior in the case of the constructor that the compiler generates for an Inner class. Why so? Is it specified by JLS?
P.S: I know that the inner class contains a hidden reference to the outer class and that reference is being set here in the above javap output. But the question is why it is set before calling super constructor. What am I missing?
Upvotes: 2
Views: 93
Reputation: 44042
An inner class is an abstraction that should be as transparent to a Java programmer as possible. Consider the following class structure and think about what would happen if you set the $this
field only after calling the inner class's super constructor.
class Foo {
Foo() { System.out.println(foo()); }
String foo() { return "foo"; }
}
class Bar {
String bar() { return "bar"; }
class Qux extends Foo {
@Override
String foo() { return bar(); }
}
}
Note how the overridden method in class Qux
calls a method of its outer class Bar
. For this to work, the $this
field that holds the Bar
instance must be set before the super constructor of Foo
is invoked. Otherwise, you would end up with a NullPointerException
as the field is not yet initialized. To make this more clear, look at the following call chain of any instanciation of a Qux
instance:
Qux() -> Foo() -> this.foo() -> $this.bar()
As a programmer that is not to familiar with the implementation of an inner class, you would wonder where this exception comes from. In order to make the inner class abstraction transparent, you must set the field first, otherwise you would be stuck with a quite leaky abstraction. I do not argue that this makes the above example a good implementation, but it is legal.
Upvotes: 1