The method f() is overridden in the subclass Sub. So, when the instance initializer executes in Super (int a = f(); ), the method f from class Sub is invoked, returning the value of Sub's instance variable b. However, we haven't yet initialized that variable because we must initializer superclasses before subclasses. Therefore, when b is returned, it is still 0, not 2. That's why you get the first 0. I hope that helps, Corey
Howdy! This is a fun one The big issue to understand here is this: Polymorphism works, even when an object is not yet fully-formed. The code you show steps into the middle of the object construction process for the instance of the subclass. Here's how it works... (I have left out a few steps which do not matter) 1) Class Test main() method begins 2) you say "new Sub()" which invokes the constructor for Sub(). And this part is crucial: when the constructor for Sub() is invoked, the value of 'b' in Sub is still 0. It has not yet been initialized to '2' (that happens only AFTER the Super constructor has finished). 3) Sub() immediately invokes the constructor of class Super() 4) When the Super() constructor is running, the a variable will be initialized. But what is it being initialized to? The return value of the f() method. HERE'S THE BIG QUESTION... *which* f() method? 5) The f() method of Sub runs! In other words, even though the Sub() constructor has not completed, and the Sub object is not yet fully formed, polymorphism still applies. So the f() method call in class Super still causes the Sub overriding version of f() to run! Because remember, it really *is* a Sub object, so the overriding method always runs. 6) Because the f() method in Sub is called *before* the Super() constructor has completed, look at what Sub's f() method returns... the value of 'b'. But the value of 'b' has not yet been initialized to its explicit value of '2', since *that* won't happen until after the Super() constructor runs to completion. So, for this brief moment in time, Sub's f() method returns the value of b, but b has not yet been set to 2, so it still has its default value of 0. 7) So the real issue is that 'a', in class Super, is assigned the return value of Sub's f() method, but BEFORE the Sub object is formed. So the value of 'a' will forever be the default value of 'b', from class Sub. So when the line sup.a runs, you see the '0' which was the result of Sub's f() method running before the Sub object was *ready* to be run. I'm hoping this makes a little sense. This is the classic "Shroedinger's object" state, when a subclass object is in the process of being formed, but part of the superclass uses methods that have been overridden by the subclass. This shows up in other issues to... for example, if you use an overridden method in the constructor of the superclass as follows:
Changing the code to what I have above will give you the same result (0 and 2), for the same reason -- the f() method that runs in the Super() constructor is the Sub overriding version of f(), NOT the one in Super(). So, we have the same problem. The Sub version of f() runs, but it returns the value of 'b' which (since the Super() constructor has not yet completed) is still set to the default value of 0. Cheers, Kathy author, Head First Java (next month, from O'Reilly) and whatever the heck my certification book is called, from Osborne/McGraw-Hill
Hi Corey, Thanks. I was expecting the overiding part of it...but I was not expecting to see b = 0...which as you explained is not yet initialized at that point, so we see 0... Is there a good document I can read up that would explain this? I get confused with this stuff
Thanks Kathy, Yet another awesome explination from you...thanks for taking the time to elaborate every step...it really helped... And by the way your absolutely COOL certification book which is the best ever is called: Sun Certified Programmer & Developer for Java 2 Study Guide (Exam 310-035 & 310-027) Thanks again, Monisha.