2

I stumbled upon this piece of code.
I tried to guess what will be the result of running it before actually doing so. I was really confused when I saw them & in need of some explanations.
This is the code:

public class A {
    String bar = "A.bar";
    A() { foo(); }

    public void foo() {
        System.out.println("A.foo(): bar = " + bar);
    }
}

public class B extends A {
    String bar = "B.bar";
    B() { foo(); }
    public void foo() {
        System.out.println("B.foo(): bar = " + bar);
    }
}

public class C {
    public static void main(String[] args) {
        A a = new B();
        System.out.println("a.bar = " + a.bar);
        a.foo();
    }
}

The output is:

B.foo(): bar = null
B.foo(): bar = B.bar
a.bar = A.bar
B.foo(): bar = B.bar

Why is that?

  • How is bar = null?
  • Why is a.bar = A.bar even appear? I haven't instantiated A at all.
  • And if A appears, why is it after B?

1 Answer 1

7

There are a few facts you ought to know before I start explaining every single step in your code's execution:

  • Field references are resolved based on reference type and method calls are resolved during run-time (in a dynamic-fashion) based on the object type.
  • super() is placed implicitly in every constructor even if you don't put it there yourself (it is not called if you call super(int x, int y) for instance).
  • It is considered very bad practice to call "override-able" methods from a constructor - you will see why when we go through the execution.

Now let's break down your code step-by-step:

  • You instantiate B by calling its default constructor B().
  • As I said before, the call to super() is added implicitly to any constructor, so A() is immediately called.
  • Inside A() you call foo(), which is overridden in class B and that is why foo() is called from B.
  • Inside B's foo() you get the output B.foo(): bar = null since Java didn't get to initialize B's fields yet (its constructor hasn't been executed yet!) and the fields of object type are initialized to null by default.
  • Now that we are done with the constructor of A() we go back to the constructor of B().
  • Inside said constructor, we have the call to foo() again, which is again B's foo(). But different from last time, B have its fields initialized (after the call to super()) properly so you get the expected B.foo(): bar = B.bar.
  • Now we are back to the warm embrace of main.
  • You access a.bar, and since as I said field references are resolved based on reference type, you get the field bar of A.
  • Lastly, for the same reason, you call a.foo() which again triggers B's foo() which prints b.bar once again.

And we are done! :)

Further references and worthwhile reading materials:
Static and dynamic binding explained
Order of constructor calls

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.