Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java constructors behavior inheritance and static/dynamic binding

So i have the following 3 classes:

A class "A" with 1 field and 1 constructor that calls a method:

public class A {
    String bar = "A.bar";

    A() {
        foo();
    }

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

A second class "B" that inherits from A with 1 field and 1 constructor that calls a method :

public class B extends A {
    String bar = "B.bar";

    B() {
        foo();
    }

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

A third class "C" that contains the main method :

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

i followed the program in debugging mode and i still can't quite figure out the output. i would appreciate a detailed explanation of the process that occurs and the principles that stand behind it. much thanks.

Edited: like people pointed out i did forget the last line of the output which is :

B.foo(): bar = B.bar

like image 212
Yarden Meshulam Avatar asked Feb 09 '23 17:02

Yarden Meshulam


2 Answers

Constructor of class B is invoked first. However, if you extend a class and not explicitly call a constructor of the base class (or some other constructor of derived class), then the first line of every default constructor is (hidden) call of super(), i.e. the constructor of base class (in this case A). So, we are proceeding with the constructor of A, which calls method foo(). Since this method is overriden in class B, overriden implementation is called. Things now become interesting - constructor of B still hasn't finished, so the value of bar is still not set, thus the output:

B.foo(): bar = null

We are now back in B's constructor, which outputs

B.foo(): bar = B.bar

Finally, the output

a.bar = A.bar

is due to variable shadowing.

like image 148
Miljen Mikic Avatar answered Feb 12 '23 08:02

Miljen Mikic


  1. You call the constructor of B
  2. Inside the constructor of B the constructor of A is being called implicit
  3. The A's constructor calls the method foo()
  4. Since the method foo() is being overwritten on B, Java resolves to this explicit method and calls this implementation instead Here you have the B.foo(): bar = null 'cause the object hasn't been initialized yet
  5. Then the constructor of B continues its execution and calls foo() again, then you have the B.foo(): bar = B.bar
  6. The constructor finalizes its execution so the next string that you have is from your System out. Here because you are using the A class to access your instance, and since the attributes of a class cannot be override on subclasses (not at least that way) Java resolves the value of bar to the value stored in A.

Here are the docs for this

Subclass attributes only hide superclass attrs. https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html

Some notes on how super works and the super constructors https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Do not call overridable methods in constructors https://www.securecoding.cert.org/confluence/display/java/MET05-J.+Ensure+that+constructors+do+not+call+overridable+methods

P.S. You missed the last method call in the output that you provide us hehe

like image 29
xiumeteo Avatar answered Feb 12 '23 08:02

xiumeteo