-
Bug
-
Resolution: Fixed
-
P4
-
7
-
None
-
rc
-
generic
-
generic
-
Verified
The following program doesn't compile (excerpt from Java Puzzlers - Puzzle 90 - 'it's absurd, it's pain, it's a superclass'):
public class Outer {
class Inner1 extends Outer {}
class Inner2 extends Inner1 {}
}
The program doesn't compile as the synthetic super call generated by javac inside Inner2's default constructor is:
this.super();
As a consequence the program is rejected with the following error:
Test.java:3: cannot reference this before supertype constructor has been called
class Inner2 extends Inner1 {}
^
This program would have compiled if javac were able to generate the following code:
Outer.this.super()
It's quite sensible not to consider the unqualified this as a suitable qualifier for a qualified super constructor call that has to be generated automatically by javac. Such a choice will always result in reference of 'this' before the superclass constructor has been called. However, this behaviour seems to be mandated by the JLS, section 8.8.7.1:
"Let C be the class being instantiated, let S be the direct superclass of C, and let i be the instance being created. The evaluation of an explicit constructor invocation proceeds as follows:
* First, if the constructor invocation statement is a superclass constructor invocation, then the immediately enclosing instance of i with respect to S (if any) must be determined. Whether or not i has an immediately enclosing instance with respect to S is determined by the superclass constructor invocation as follows:
o If S is not an inner class, or if the declaration of S occurs in a static context, no immediately enclosing instance of i with respect to S exists. A compile-time error occurs if the superclass constructor invocation is a qualified superclass constructor invocation.
o Otherwise:
+ If the superclass constructor invocation is qualified, then the Primary expression p immediately preceding ".super" is evaluated. If the primary expression evaluates to null, a NullPointerException is raised, and the superclass constructor invocation completes abruptly. Otherwise, the result of this evaluation is the immediately enclosing instance of i with respect to S. Let O be the immediately lexically enclosing class of S; it is a compile-time error if the type of p is not O or a subclass of O.
+ Otherwise:
# If S is a local class (§14.3), then let O be the innermost lexically enclosing class of S. 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.
# Otherwise, S is an inner member class (§8.5). It is a compile-time error if S is not a member of a lexically enclosing class, or of a superclass or superinterface thereof. 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
[...] "
The last bullet explicitely states that, in this particular cases, javac should exploit the 0th-this as a qualifier for the super expression (this is because Inner2 is a subtype of Outer which encloses Inner1). I think that the lookup should start from the innermost lexically enclosing class of Inner2 (Outer) - thus skipping Inner2 completely. The change in the compiler to do so is quite trivial, and I think it would make the use of inner class more intuitive for the end-user.
This change seems also the only way for fixing a compiler regression bug (6541876) without restricting the space of the programs accepted by javac (e.g. fix for 4903103 should be invalidated if no JLS change is in sight).
public class Outer {
class Inner1 extends Outer {}
class Inner2 extends Inner1 {}
}
The program doesn't compile as the synthetic super call generated by javac inside Inner2's default constructor is:
this.super();
As a consequence the program is rejected with the following error:
Test.java:3: cannot reference this before supertype constructor has been called
class Inner2 extends Inner1 {}
^
This program would have compiled if javac were able to generate the following code:
Outer.this.super()
It's quite sensible not to consider the unqualified this as a suitable qualifier for a qualified super constructor call that has to be generated automatically by javac. Such a choice will always result in reference of 'this' before the superclass constructor has been called. However, this behaviour seems to be mandated by the JLS, section 8.8.7.1:
"Let C be the class being instantiated, let S be the direct superclass of C, and let i be the instance being created. The evaluation of an explicit constructor invocation proceeds as follows:
* First, if the constructor invocation statement is a superclass constructor invocation, then the immediately enclosing instance of i with respect to S (if any) must be determined. Whether or not i has an immediately enclosing instance with respect to S is determined by the superclass constructor invocation as follows:
o If S is not an inner class, or if the declaration of S occurs in a static context, no immediately enclosing instance of i with respect to S exists. A compile-time error occurs if the superclass constructor invocation is a qualified superclass constructor invocation.
o Otherwise:
+ If the superclass constructor invocation is qualified, then the Primary expression p immediately preceding ".super" is evaluated. If the primary expression evaluates to null, a NullPointerException is raised, and the superclass constructor invocation completes abruptly. Otherwise, the result of this evaluation is the immediately enclosing instance of i with respect to S. Let O be the immediately lexically enclosing class of S; it is a compile-time error if the type of p is not O or a subclass of O.
+ Otherwise:
# If S is a local class (§14.3), then let O be the innermost lexically enclosing class of S. 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.
# Otherwise, S is an inner member class (§8.5). It is a compile-time error if S is not a member of a lexically enclosing class, or of a superclass or superinterface thereof. 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
[...] "
The last bullet explicitely states that, in this particular cases, javac should exploit the 0th-this as a qualifier for the super expression (this is because Inner2 is a subtype of Outer which encloses Inner1). I think that the lookup should start from the innermost lexically enclosing class of Inner2 (Outer) - thus skipping Inner2 completely. The change in the compiler to do so is quite trivial, and I think it would make the use of inner class more intuitive for the end-user.
This change seems also the only way for fixing a compiler regression bug (6541876) without restricting the space of the programs accepted by javac (e.g. fix for 4903103 should be invalidated if no JLS change is in sight).
- relates to
-
JDK-4903103 Can't compile subclasses of inner classes
-
- Closed
-
-
JDK-6541876 "Enclosing Instance" error new in 1.6
-
- Closed
-
-
JDK-8157994 8.8.7.1: Account for an inherited supertype S
-
- Closed
-