Eric said:
public class A {
public A() {}
public class B extends A{
public B(){}
}
public class C extends B{
public C(){} // <- error happens here
}
}
I suspect that it's a bug in the compiler.
All the following is using JDK 1.5.0.
The compiler ought to treat the above as if it said (roughly):
public class A
{
public A() {}
}
class B extends A
{
private final A m_outer;
public B(A theA)
{
super();
m_outer = theA;
}
}
class C extends B
{
private final A m_outer;
public C(A theA)
{
super(theA);
m_outer = theA;
}
}
(but with B and C called "A$B" and "A$C" respectively). And that does compile
OK.
There is a problem with that, however, which is that if the compiler generated
code exactly like the above, methods called by A.B's constructor that had been
overridden in A,C would "see" A.B.m_outer with the wrong value (null), which is
illegal according to the Java spec (a bug in the language design if you ask me,
but there it is). So what the compiler actually wants to generate is more
like:
class B extends A
{
private final A m_outer;
public B(A theA)
{
m_outer = theA;
super();
}
}
class C extends B
{
private final A m_outer;
public C(A theA)
{
m_outer = theA;
super(theA);
}
}
The difference is that the assignment to m_outer is placed /before/ the call to
the superclass constructor. Doing that explicitly in Java would be illegal,
but the compiler allows itself to break its own rules (a sign of just how slimy
this hack is), and there's also a special hack in the verifier to allow the
corresponding bytecode sequence, which would otherwise be illegal. (And BTW,
if you compile for an early enough JVM, -target 1.2, say -- before the change
to the verifier, then javac will produce different code that puts the
assignment after the superclass constructor.)
It seems that the compiler will allow itself to generate the code for A.B's
hacky constructor, but not the very similar code for A.C. That /might/ be by
design, but I suspect not. Principally because if you remove the declaration
that A.B is a subclass of A, then it all works as you'd expect. There's no
obvious reason why making A.B a subclass of A should be /intended/ to interact
with the complicated hack, so I suspect it's just a bug.
BTW, if I create the bytecodes directly that I think the compiler /should/ be
generating, then the resulting classfile runs fine, so the JVM verifier is
happy enough with this situation. Another reason to suspect that its a bug in
the compiler.
-- chris