Re: calling own methods from constructor
On 2011-04-07 18:15:23 -0400, Jim Janney said:
Lew <lew@lewscanon.com> writes:
Janney wrote:
In Java evaluating new Bar() will throw an exception. ?But in C++ the
equivalent code would print
Override me! I dare you.
Two guys walk into a...
In effect, until the constructor of Foo completes, the object is
considered to be an instance of Foo, so calls to virtualMethod() to to
Foo.virtualMethod even if it has been overridden. ?After the super
constructor completes, the object is treated as an instance of Bar, so
evaluating new Bar().virtualMethod() would print two lines and then
throw an exception.
I've been surprised by this behaviour in C++ enough times that I'm not
sure that it has the better approach. ?But a solution does exist.
A solution to what, exactly?
Lew, bog off.
In the message I was replying to, in the text that you deleted, Owen
Jacobsen correctly observed that
Jacobson. Like the UML guy, to my eternal mortification. :)
It's hard to prove that a constructor never calls a virtual method. Consider:
This is a technical issue, and I observed that other languages have
found ways to prevent virtual methods from being called before their
owning objects have been fully constructed. One can debate whether this
desirable, but C++ provides an existence proof that it's possible.
Sure. C++'s objection initialization proceeds from the top down, just
like Java's, with the difference that the class of '*this' changes as
constructors complete. In your extension of my example, with
public class Foo { /* ... */ }
public class Bar extends Foo { /* ... */ }
this would mean that during Foo's constructor, 'this' points to a Foo
object, while during Bar's constructor, it points to a Bar object*.
However, in Java, constructor chaining is syntactically a statement, so
it appears that control flow begins in the most-derived class's
constructor before chaining upwards through each superclass's
constructor to Object() -- and that's exactly what happens under the
hood!
$ cat Surprise.java
class Surprise {
public Surprise(String s) {
super(); // explicit for the sake of discussion only.
}
}
class Subprise extends Surprise {
public Subprise() {
super("Hello, world!");
}
}
$ javap -classpath . Subprise -c
Compiled from "Surprise.java"
class Subprise extends Surprise{
public Subprise();
Code:
0: aload_0
1: ldc #1; //String Hello, world!
3: invokespecial #2; //Method Surprise."<init>":(Ljava/lang/String;)V
6: return
}
Given that JVM-level implementation of constructors, having the class
of 'this' change after the third instruction would be very tricky to
implement. C++ doesn't have this problem, since (a) constructor
chaining is NOT syntactically like a statement and (b) constructor
chaining doesn't have to compile like one, either.
Could it have been designed differently? Sure. Java's constructor
semantics are a weird-but-mostly-intuitive mix of C++'s constructors
and Smalltalk's initializers-are-just-methods approach (where you're
not forced to chain to a parent class's initializer at all). It's a
compromise, and like all compromises, it's not quite like any of the
alternatives; however, I think having the type of 'this' remain stable
is a useful feature. :)
-o
* And also that if Foo.Foo() captures 'this' in a field, Bar.Bar() can
compare this to the field using == and get true back, even though the
class has changed. 'new' is only allowed to introduce one distinct new
pointer.