What happens if you call an overridden method using super in a constructor

白昼怎懂夜的黑 提交于 2019-12-09 16:58:07

问题


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 ?


回答1:


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.




回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/33398294/what-happens-if-you-call-an-overridden-method-using-super-in-a-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!