M
Mike Schilling
Take the following code:
public class Outer {
String className;
public static void main(String[] args) {
Outer amb = new Outer();
SubOuter sa = amb.new SubOuter();
SubOuter.SubInner subIn = sa.new SubInner();
System.out.println(subIn.getSuperEncloserName());
}
Outer() {
className = "Outer";
}
class Inner {
String getEncloserName() {
return className;
}
}
class SubOuter extends Outer {
SubOuter() {
className = "SubOuter";
}
class SubInner extends Inner {
SubInner() {
super(); // ******
}
String getSuperEncloserName() {
return super.getEncloserName();
}
}
}
}
When compiled with either javac or jikes and then run, the output is
"SubOuter".
Let's analyze what happens at the starred line. We must determine the
immediately enclosing instance of "this" with respect to class Inner.
"this", an instance of SubInner, has an immediately enclosing instance
SubOuter.this, which in turn has an immediately enclosing instance
Outer.this. According to JLS 8.8.5.1:
Let O be the innermost lexically enclosing class of which S is a member,
and let n be an integer such that O is the nth lexically enclosing class
of C.
The immediately enclosing instance of i with respect to S is the nth
lexically
enclosing instance of this.
Here, S is Inner, O Outer, and C SubInner. By this rule, the enclosing
instance should be Outer.this (the 2nd lexically enclosing instance of
"this"), since the type of the selected enclosing instance of "this" must
exactly match the innermost lexically enclosing class of S. As running the
code shows, the enclosing instance is actually SubOuter.this (the first
lexically enclosing instance of "this".) This is what I would have naively
assumed: that the enclosing instance of "this" needs to be assignment
compatible with the enclosing class of S, not that it needs to be identical.
In other words, one would expect the following to compile correctly:
public class Outer2 {
class Inner {
}
}
public class SubOuter2 extends Outer2 {
class SubInner extends Outer2.Inner {
SubInner() {
super();
}
}
}
In fact it does. Qualifying the super call as "Outer2.this.super()" is not
required.
Thus, 8.8.5.1 should read something like:
Let O be the innermost lexically enclosing class of which S is a member,
and let n be the smallest integer such that O or a subclass of O is the
nth
lexically enclosing class of C
The immediately enclosing instance of i with respect to S is the nth
lexically
enclosing instance of this.
This matches both our expectations and what seems to be implemented by javac
and jikes. Note that a similar correction is needed in 15.9.2, which
describes the behavior of the constructor of an anonymous class whose
superclass in an inner class.
public class Outer {
String className;
public static void main(String[] args) {
Outer amb = new Outer();
SubOuter sa = amb.new SubOuter();
SubOuter.SubInner subIn = sa.new SubInner();
System.out.println(subIn.getSuperEncloserName());
}
Outer() {
className = "Outer";
}
class Inner {
String getEncloserName() {
return className;
}
}
class SubOuter extends Outer {
SubOuter() {
className = "SubOuter";
}
class SubInner extends Inner {
SubInner() {
super(); // ******
}
String getSuperEncloserName() {
return super.getEncloserName();
}
}
}
}
When compiled with either javac or jikes and then run, the output is
"SubOuter".
Let's analyze what happens at the starred line. We must determine the
immediately enclosing instance of "this" with respect to class Inner.
"this", an instance of SubInner, has an immediately enclosing instance
SubOuter.this, which in turn has an immediately enclosing instance
Outer.this. According to JLS 8.8.5.1:
Let O be the innermost lexically enclosing class of which S is a member,
and let n be an integer such that O is the nth lexically enclosing class
of C.
The immediately enclosing instance of i with respect to S is the nth
lexically
enclosing instance of this.
Here, S is Inner, O Outer, and C SubInner. By this rule, the enclosing
instance should be Outer.this (the 2nd lexically enclosing instance of
"this"), since the type of the selected enclosing instance of "this" must
exactly match the innermost lexically enclosing class of S. As running the
code shows, the enclosing instance is actually SubOuter.this (the first
lexically enclosing instance of "this".) This is what I would have naively
assumed: that the enclosing instance of "this" needs to be assignment
compatible with the enclosing class of S, not that it needs to be identical.
In other words, one would expect the following to compile correctly:
public class Outer2 {
class Inner {
}
}
public class SubOuter2 extends Outer2 {
class SubInner extends Outer2.Inner {
SubInner() {
super();
}
}
}
In fact it does. Qualifying the super call as "Outer2.this.super()" is not
required.
Thus, 8.8.5.1 should read something like:
Let O be the innermost lexically enclosing class of which S is a member,
and let n be the smallest integer such that O or a subclass of O is the
nth
lexically enclosing class of C
The immediately enclosing instance of i with respect to S is the nth
lexically
enclosing instance of this.
This matches both our expectations and what seems to be implemented by javac
and jikes. Note that a similar correction is needed in 15.9.2, which
describes the behavior of the constructor of an anonymous class whose
superclass in an inner class.