From the domain of stuff that makes you wonder if your debugger is lying to you, I give you a little puzzle. How many times does the following code print the line “Creating crumble…” and why?
public class JavaLanguagePuzzle1
{
private static abstract class CookieBase
{
public CookieBase()
{
init();
}
protected void init() {}
}
private static class Cookie extends CookieBase
{
private Crumble crumble = null;
protected void init()
{
crumble();
}
public Crumble crumble()
{
if( this.crumble == null )
{
this.crumble = new Crumble();
}
return crumble;
}
}
private static class Crumble
{
public Crumble()
{
System.err.println( "Creating crumble..." );
}
}
public static void main( final String[] args )
{
( new Cookie() ).crumble();
}
}
SOLUTION: Pretty much everyone who responded came up with the correct answer of two times. Most correctly explained why that happens. Part of the reason is that explicit member variable initialization in a class happens after the superclass constructor has executed. The part that might be tricky to realize is that explicitly initializing a member variable to null does not have the same runtime behavior as leaving it uninitialized. Removing explicit initialization of crumble to null makes this code behave as it appears to on the surface.
23 comments:
Twice,once for the inherited constructor from the superclass, because the method Cookie#init() overrides the method CookieBase#init(), and the other when the method Cookie#crumble() is called
Twice, becaus the field crumble in class Cookie is explicitly set to null. This happens after the call to the superclass constructor. Two lessons: don't explicitly initialize fields to their default values, and don't call methods from constructors that can be overridden by subclasses.
Twice. Once for the constructor in CookieBase. Once for the explicit call to crumble.
Twice of course.
ANSWER:
Twice. The reason is that it calls super(); before making assignments to your fields. The logic goes like this:
Call Cookie.NEW
Call CookieBase.NEW
Call CookieBase.init()
Call Cookie.init()
Cookie.crumble == null -> Cookie.crumble = new Crumble()
CookieBase.Finish init()
CookieBase.Finish NEW
Cookie.Assign null -> crumble
Finish NEW
I hope that explains it. If you change the field declaration to:
private Crumble crumble;
Then it will print it out once, as expected.
That's interesting. I probably wouldn't have picked that, intuitively, as it doesn't leap out as the right way of doing things, but there we go.
the probably most often asked job interview questions are about class initialization as far as i experienced it. so its a good idea to learn this by hard ;)
I guessed 1 of course, but after stepping through the code, I quickly recognized the cause. Why, because the same code structure has given me many NPEs. In general you assume that all fields already have been initialized when a "normal" method is called. However, if you override a method that is called from the superclass' constructor, the initialisation code in the subclass will not have run yet. The crucial thing to understand is that the initialisation code is invisibly inserted into constructors after the implicit or explicit call to super().
The order will be
1. superclass initialisation
2. superclass constructor
3. subclass method m()
4. subclass initialisation
5. subclass constructor
Hence, the initialisation that is performed in step 3 is clobbered by step 4.
Without trying it, I would have said once. When the new Cookie is created in main(), init() is called from Cookiebase which is a template method and only implemented in Cookie. Init calls crumble which initializes the internal crumble attribute of Cookie and prints "creating crumble". After that, I would think, crumble shouldn't be created anymore subsequently due to the == null check.
But the mere fact that this is a Java puzzle probably indicates that I am wrong...
Thanks for this one, I learnt something today.
Hi,
it is printed twice, of course. The reason is that the first print is made during initialization of the super class (CookieBase), so even when the local field Cookie.crumble is set then, it will be reset to null when superclass finishes its initialization, and Cookie class will be processed - then its fields are initialized - in this case crumble = null again.
But that's something you usually don't think about ;-)
From my head without using a compiler... this is the sequence I look into things
1. there is no static initialization to care of, apart ftom the main function.
2. I see public CookieBase()
{
init();
}
protected void init() {}
And I remember "never ever call a method in a constructor, that is not set final". Because I did this many years ago and run into big trouble. Because subclasses are not initialized, when you do this.
So this is what happens (still in my head):
init calls crumble() in a class that is not initialized, therfore the variable crumble doesn't have the value null, instead it is not initialized.
The if statement returns false (not initialized is not null), no Crumble is initialized ever, no line is printed.
right?
Gerd
Twice: instance initializers (crumble = null) are called after superconstructors.
The reason is that the implicit constructor in Cookie does the following:
- Calls the superconstructor in CookieBase (calls init, outputs Creating Crumble). Crumble is set to be the new Crumble().
- initializes type instance variables, setting crumble to null again.
The call to #crumble() then creates a new crumble, outputting the string a second time.
Now I'm hungry - do I win a cookie? :-)
-John Hawksley
Twice. One gets called by CookieBase# which calls CookieBase#init() which is overridden by Cookie#init(), which calls Cookie#crumble(); that sets the Cookie#crumble field. However, Cookie# has not been called yet, as soon as it starts it sets Cookie#crumble to null...
Here's the kicker though: if you remove the '= null' initializer, it works as expected.
Twice.
Once from the init() call in the CookieBase consturctor
and once again from the crumble() call, because the Cookie constructor nulls the Cookie.crumble field after calling the super constructor.
Super constructors are called before fields are initialized. Hence your (superfluous) field initialization overwrites the already set value:
private Crumble crumble = null;
You get only one printout with:
private Crumble crumble;
Note that it's generally disencouraged to call methods from constructors that can be overwritten in sub classes for this reason.
I guess that's the reason NetBeans 6.9 is issuing warnings when using overloaded methods in constructors...
The subclass attributes modified while executing the super's constructor are overwritten by the subclass constructor when it's executed right after.
I didn't run it, but likely it will print it two times, cause' you are nulling the field in child constructor after it is set trough the parent's constructor call.
At the first glance, it looks like it will print the statement twice - first time during the constructor call, and the second time during the call to crumble(). As far as I remember, the declaration assignments get executed after the constructor, so
this.crumble = new Crumble();
will be nullified by
private Crumble crumble = null;
when executed in CookieBase() -> init() -> crumble()
2 times: the line "private Crumble crumble = null;" is executed AFTER the super class calls init() and so it will set crumble to null.
This happens because objects's methods are always executed AFTER all super code.
Twice.
- CookieBase constructor calls init(), which calls new crumble(), which sets crumble to a new Crumble(), which prints.
- Cookie constructor initializes crumble to null
- crumble() is called, which again sets crumble to a new Crumble, which prints a second time.
Twice. The reason is that the initializer (private Crumble crumble = null) is called after the constructor. Since init() is called by the constructor, the flow goes like this:
CookieBase()
-> init()
-> crumble()
-> this.crumble = new Crumble()
this.crumble = null
Twice (to my surprise).
It's because the implicit initialization of the superclass is completed before the initialization of the subclass (including the
explicit initialization to null of the private field). Simply eliminating the "= null" gets rid of the behaviour.
Nice puzzle!
RJW.
Ans: Twice
first print:It overrides the method init()
second print: when method crumble() is called,value of variable 'crumble' is null.
no need to call super class constructor.
Post a Comment