Path: csiph.com!x330-a1.tempe.blueboxinc.net!newsfeed.hal-mli.net!feeder1.hal-mli.net!nx01.iad01.newshosting.com!209.197.12.246.MISMATCH!nx02.iad01.newshosting.com!newshosting.com!69.16.185.11.MISMATCH!npeer01.iad.highwinds-media.com!news.highwinds-media.com!feed-me.highwinds-media.com!post01.iad.highwinds-media.com!newsfe20.iad.POSTED!00000000!not-for-mail From: Owen Jacobson Newsgroups: comp.lang.java.programmer Message-ID: <2011040723213981856-angrybaldguy@gmailcom> References: <2011040622233261380-angrybaldguy@gmailcom> <2pzko2f2nx.fsf@shell.xmission.com> <2pwrj56744.fsf@shell.xmission.com> MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1; format=flowed Content-Transfer-Encoding: 8bit Subject: Re: calling own methods from constructor User-Agent: Unison/2.1.4 Lines: 100 X-Complaints-To: abuse@UsenetServer.com NNTP-Posting-Date: Fri, 08 Apr 2011 03:21:40 UTC Date: Thu, 7 Apr 2011 23:21:39 -0400 Xref: x330-a1.tempe.blueboxinc.net comp.lang.java.programmer:2971 On 2011-04-07 18:15:23 -0400, Jim Janney said: > Lew 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."":(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.