Constructor Chaining Conundrum

R

rnurse

Hi All,

I'm confused by this code snippet:


class Base
{
String s = "BaseStr";
void print( )
{
System.out.println("Super " + s);
}

Base( )
{
print( );
}
}


public class Derived extends Base
{
String s = "DerivedStr";
void print( )
{
System.out.println("Sub " + s);
}

Derived( )
{
}

public static void main(String [] args)
{
Base b = (Base) new Derived( );
}
}


What I'm thinking should happen is that when the Derived constructor,
Derived(), is called, a call to super() is made. This call then leads
to the no-arg constructor of it's parent class Base: Base(). Base()
calls super() which is the no-arg constructor of its parent Object:
Object( ). Object( ) completes and then control is passed to Base( )
which should then call its print( ) method and print out "Super
BaseStr". After Base( ) runs Derive( ) completes. Therefore the
output should be "Super BaseStr". Yet, the output is "Sub null". What
gives? And, why is s = null at this point. Should s have been
initialized to "DerivedStr" by now?
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

(e-mail address removed) schreef:
Hi All,

I'm confused by this code snippet:


class Base
{
String s = "BaseStr";
void print( )
{
System.out.println("Super " + s);
}

Base( )
{
print( );
}
}


public class Derived extends Base
{
String s = "DerivedStr";
void print( )
{
System.out.println("Sub " + s);
}

Derived( )
{
}

public static void main(String [] args)
{
Base b = (Base) new Derived( );
}
}


What I'm thinking should happen is that when the Derived constructor,
Derived(), is called, a call to super() is made. This call then leads
to the no-arg constructor of it's parent class Base: Base(). Base()
calls super() which is the no-arg constructor of its parent Object:
Object( ). Object( ) completes and then control is passed to Base( )
which should then call its print( ) method and print out "Super
BaseStr". After Base( ) runs Derive( ) completes. Therefore the
output should be "Super BaseStr". Yet, the output is "Sub null". What
gives? And, why is s = null at this point. Should s have been
initialized to "DerivedStr" by now?

Why doesn’t this surprise me? Have you ran it through a debugger? You
will see exactly what happens: s in Derived *shadows* s in Base, whereas
print() in Derived *overrides* print() in Base; thus if Base’s
constructor calls print(), Derived’s print() will be called (we are
creating a Derived, after all, the unnecessary cast doesn’t change
anything at that, that’s polymorphism at work), which uses its own s,
shadowing Base’s s, and at that point, the initialisation of s, which
happens *after* calling super(), has not yet been done, thus Derived.s
is null, and you get Sub null in the console.

This is all like it should be. Golden rule: do not call methods in a
constructor which can be overridden in a subclass.

Also see http://mindprod.com/jgloss/constructor.html#INITIALISATION and
http://mindprod.com/jgloss/shadow.html

H.
- --
Hendrik Maryns

==================
http://aouw.org
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (GNU/Linux)

iD8DBQFEfuW3e+7xMGD3itQRAuBPAJ9tR7Wvy1eTEwCBwHK00zLk9B2ftQCeJlHm
CEnUnSLfNta4Curm73ecgP8=
=2D1A
-----END PGP SIGNATURE-----
 
C

Chris Uppal

class Base
{
String s = "BaseStr";
public class Derived extends Base
{
String s = "DerivedStr";

These are two independent variables, albeit both called "s". Thus each
instance of derived contains /two/ String-valued fields, one inherited from
Base, and the other defined in Derived. Since they share a name (not a good
idea), when methods defined in Base refer to "s" they access the field defined
in Base, but when methods in Derived refer to "s" they will access the field in
Derived.

The second part of the puzzle is that explicit initialisation of instance
fields is implemented by the compiler emitting code to do the initialisation in
the derived class's constructor(s) immediately /after/ the call to the
superclass constructor. So, during the execution of Base's constructor, Base.s
is set to "BaseStr", but Derived.s is still null.

Lastly, since Derived.print() overrides the definition in Base, when the Base
constructor calls print(), that will actually invoke Derived.print(). Since
that method refers to Derived.s, that is the value which will be printed. And
that value has not yet been set to "DerivedStr" because the Base constructor
has not yet returned.

-- chris
 
C

Chris Uppal

Hendrik said:
Golden rule: do not call methods in a
constructor which can be overridden in a subclass.

I think it's more of a Copper rule, or possibly Tin ;-)

It's perfectly OK to call overridable/overriden methods from constructors /if
you do it by design/. For instance, to get the effect that I think the OP is
aiming for:

class Base
{
String s = initialValueForS();

protected String initialValueForS() { return "BaseStr"; }

// ...other stuff clipped...
}

class Derived
extends Base
{
protected String initialValueForS() { return "DerivedStr"; }

// ...etc...
}

That uses an overriden method to define the inital value given to the shared
variable "s". (The assignment isn't textually in the body of the constuctor,
but it is semantically.)

-- chris
 
R

rnurse

Ahh, the fog has lifted. I added a new method to the Base class: one
that is not overriden by the child. I then have the Base constructor
call it instead.

Base()
{
// print( );
anotherPrint();
}
void anotherPrint( )
{
System.out.println("Super " + s);
}

Now, I'm getting the behavior I was expecting. I'm studying for the
SCJP and this was one of the Whizlabs testing simulator
(www.whizlabs.com) questions. Thanks for your help and those links.
I'll also be using my debugger as a study aid too.
 
R

rnurse

Chris said:
The second part of the puzzle is that explicit initialisation of instance
fields is implemented by the compiler emitting code to do the initialisation in
the derived class's constructor(s) immediately /after/ the call to the
superclass constructor. So, during the execution of Base's constructor, Base.s
is set to "BaseStr", but Derived.s is still null.

I was confused by this. I wasn't sure when those instance variables
got initialized.

Thanks.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top