9

There is two classes Super1 and Sub1

Super1.class

public class Super1 {
    Super1 (){
        this.printThree();
    }

    public void printThree(){
        System.out.println("Print Three");
    }    
}

Sub1.class

public class Sub1 extends Super1 {
    Sub1 (){
        super.printThree();
    }

    int three=(int) Math.PI;

    public void printThree(){
        System.out.println(three);
    }

    public static void main(String ...a){
         new Sub1().printThree();
    }
}

When I invoke the method printThree of class Sub1 I expected the output to be:

Print Three
3

Because Sub1 constructor calling the super.printThree();.

But I actually get

0
Print Three
3

I know 0 is default value of int but how it is happening ?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
Roushan
  • 4,074
  • 3
  • 21
  • 38
  • a constructor calls super(); implicit. – Tobi Oct 28 '15 at 18:04
  • 2
    I want to add that you should never call a method in your constructor that can be overridden (exactly like in your case). Why? Your overridden method is using a field that is not set (three field). – Apokai Oct 28 '15 at 18:11
  • You got pretty good answers already, but i would also add this is perfect example why composition is always preferable over inheritance, if situation allows it. Inheritance is wolf in sheep clothing. – The Law Oct 28 '15 at 18:26

3 Answers3

17

You're seeing the effects of three things:

  1. Default super-constructor calls, and

  2. Instance initializers relative to super calls, and

  3. How overridden methods work

Your Sub1 constructor is really this:

Sub1(){
    super();               // <== Default super() call, inserted by the compiler
    three=(int) Math.PI;   // <== Instance initializers are really inserted
                           // into constructors by the compiler
    super.printThree();
}

(Surprising, I know, but it's true. Use javap -c YourClass to look. :-) )

The reason it looks like that is that the superclass must have a chance to initialize its part of the object before the subclass can initialize its part of the object. So you get this kind of interwoven effect.

And given that that's what Sub1 really looks like, let's walk through it:

  1. The JVM creates the instance and sets all instance fields to their defaults (all bits off). So at this point, the three field exists, and has the value 0.

  2. The JVM calls Sub1.

  3. Sub1 immediately calls super() (Super1), which...

    1. ...calls printThree. Since printThree is overridden, even though the call to it is in the code for Super1, it's the overridden method (the one in Sub1) that gets called. This is part of how Java implements polymorphism. Since three's instance initializer hasn't been run yet, three contains 0, and that's what gets output.

    2. Super1 returns.

  4. Back in Sub1, the instance initializer code for three that the compiler inserted (relocated, really) runs and gives three a new value.

  5. Sub1 calls printThree. Since three's instance initializer code has now run, printThree prints 3.

With regard to this instance initializer code getting moved into the constructor, you might be wondering: What if I have more than one constructor? Which one does the code get moved into? The answer is that the compiler duplicates the code into each constructor. (You can see that in javap -c, too.) (If you have a really complicated instance initializer, I wouldn't be surprised if the compiler effectively turned it into a method, but I haven't looked.)

It's a bit clearer if you do something really naughty and call a method during your instance init: (live copy)

class Super
{
    public static void main (String[] args) {
        new Sub();
    }

    Super() {
        System.out.println("Super constructor");
        this.printThree();
    }

    protected void printThree() {
        System.out.println("Super's printThree");
    }
}
class Sub extends Super
{
    int three = this.initThree();

    Sub() {
        this.printThree();
    }

    private int initThree() {
        System.out.println("Sub's initThree");
        return 3;
    }

    protected void printThree() {
        System.out.println("Sub's printThree: " + this.three);
    }
}

Output:

Super constructor
Sub's printThree: 0
Sub's initThree
Sub's printThree: 3

Note where "Sub's initThree" came in that sequence.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
3

When the instance is created, the Sub1 constructor is called.

The first instruction in any constructor is a call to the superclass constructor. If you don't have an explicit call, there will be an implicit call to the no-args constructor of Super1.

The no-args constructor is calling this.printThree(). This method is overridden in Sub1. Now, this part may be confusing, but even if the code is in the superclass, this.method() still refers to the overriding method.

So it's calling the printThree() in Sub1, which prints the uninitialized value of the variable three - 0.

Now that the superclass's constructor is done, it completes Sub1 constructor, which uses super.printThree(). Since it specifically says super, the method from Super1 is used rather than the overriding one. This prints the Print Three.

Now the Sub1 constructor is also done, and main calls the new instance's printThree(). Now three is already initialized, so you get the output 3.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • that is good one but in super1 class i am using this keyword for calling the current class method i.e, of super1#printThree but u r stated above its called the sub1#printThree by technique polymorphism then what is the meaning of this keyword here? – Roushan Oct 28 '15 at 19:07
  • The meaning is that you are using the `printThree()` of the current object, rather than some other object (if you had any other object). `this` always refers to the actual runtime class of the object, which is `Sub1`. – RealSkeptic Oct 28 '15 at 19:14
  • sorry but still i had confusion :( – Roushan Oct 28 '15 at 19:15
  • It means "check what implementation of `printThree` is currently, at runtime, in this object, and use that implementation". So because at runtime "this object" is an object of type `Sub1`, the implementation that will be ran is the one from `Sub1`. – RealSkeptic Oct 28 '15 at 19:22
0

While previous answers gave you clear answer to what is happening, they did not gave you any pointers on how to avoid your problems in the future, so I would also like to add my input on this.

If you are going to inherit, then you should make the super class constructor as "dumb" as possible. For example

public class Super{
private int a,b;
 public Super(int a, int b) {
 this.a = a;
 this.b = b;
 }
//all the methods operating on the data provided by constructor
}

and then having sub constructor like this

  private int c,d;
    public Sub(int a, int b) {
    super(a,b);
    c = a;
    d = b;
    }

Is perfectly fine and is going to give you minimal side-effects, while keeping the functionality of the parent class.

But having

public Super(){
method1();
method2();
}

and then having sub do this

public Sub(){
super.method1();
super.method2();
}

Is really asking for trouble and possible hard to track bugs. The less the object does during initialization, the better, because it gives the childs flexibility. Managing inheritance is like being dumb manager vs clever manager. Dumb manager calls Tim and Tracy employee, because they are both employees, and their jobs as Accountant and HR manager are just tags. Clever manager knows Tim and Tracy are Accountant and Manager and does not care that much that they are basically just employees.

The Law
  • 344
  • 3
  • 20