A subtle point regarding how the language selects the implicit outer instance
for an inner class creation expression has arisen in the context of compiler
bugs 4635044 and 4494653. The JLS disagrees with every Java language
implementation that I could find, and I suggest that we consider changing the
language specification instead of all the compilers (and perhaps any user code
and test cases that depend on the compilers current behavior). I have tested
javac 1.2, javac 1.3 and later, EDG's compiler, jikes, and the aspectj compiler.
The all fail the following two test cases in precisely the same way. Based on
the current wording in the JLS, the diagnostic generated by the compiler for
4494653 is misleading.
The current wording is in 15.9.2 selects the nearest enclosing instance of which
the class being created is a member. Existing compilers, on the other hand,
select the nearest enclosing class that is a subtype of the type containing the
class being created. There is only a difference when the class being created
is not inherited either because of protection or because it is hidden. It is
unfortunate that the Java language is defined in such a way that a distinction
between these two choices is possible. The two enclosed test cases, based on
the current JLS2 wording, illustrate these issues. All compilers fail them.
While the specification is clear in this regard, I wonder if it would not be
better to change the language specification to bring it in line with current
practice rather than the other way around. In order to address the related
compiler bugs, I will need a definitive statement one way or another on whether
the JLS2 will be adjusted.
/** Check that non-inheritance (private) prevents enclosing class selection. */
class T1 {
boolean isT() { return true; }
private class Middle extends T1 {
boolean isT() { return false; }
boolean enclIsT() { return T1.this.isT(); }
class Inner {}
boolean check() {
return /*T1.this.*/new T1.Middle().enclIsT();
}
}
public static void main(String[] args) {
T1 t = new T1();
Middle m = t.new Middle();
if (!m.check()) throw new Error();
}
}
/** Check that hiding prevents enclosing class selection. */
class T2 {
boolean isT() { return true; }
class Middle {
boolean enclIsT() { return T2.this.isT(); }
}
class X extends T2 {
boolean isT() { return false; }
class Middle {} // hide T2.Middle
boolean check() {
return /*T2.this.*/new T2.Middle().enclIsT();
// error: javac uses X.this instead of T2.this, above
}
}
public static void main(String[] args) {
T2 t = new T2();
X x = t.new X();
if (!x.check()) throw new Error();
}
}