1

When I run the following program

public class Example {
    static class A {
        A()
        {
            f();
        }
        public void f(){
            System.out.println("A ctor");
        }
    }
    static class B extends A {
        B()
        {
            f();
        }
        public void f() {
            System.out.println("B ctor");
        }
    }
    public static void main(String[] args) {
        B b = new B();
        b.f();
        A a = new A();
        a.f();
    }
}

I expect the following

A ctor
B ctor
B ctor
A ctor
A ctor

But instead I get

B ctor
B ctor
B ctor
A ctor
A ctor

I'm confused as to why this is. The first call to the constructor of B should call A.f() (thru the automatic call to A's constructor) but looks like its calling B.f()

Why?

2
  • All methods in Java are virtual. As such, B will never call A's version of f(). Commented May 31, 2020 at 6:24
  • Java is different from C++ in this regard. Your expectation would be correct in C++, but it isn't in Java. In thisnstuai On Java will always call the override. Commented May 31, 2020 at 6:28

1 Answer 1

1

When you compile B.java, the compiler inserts some code. While you typed.

    B() {
        f();
    }

If you reverse the Java Bytecode, you will clearly see instructions like

    B() {
        super();
        f();
    }

But while that might explain the inheritence question, there's a massive problem with your code.

It is not safe to call functions on a class that's not constructed.

So calling f() inside of A() is not safe. This is because A() isn't full constructed until the constructor of A() completes. Again, calling f() inside of B() is not safe, for the same reasons.

If you convert f() to static members, which don't use the this references, then you will have safe code; however, you won't get polymorphisim in your f() calls.

Remember, that a f() call is a call on the object. In the middle of wiring the object's dynamic methods, a call to f() might refer to either A's or B's implementation, and at the time of construction, it's not always possible to know which one should be invoked, as A()'s constructor is called in an identical manner both inside and outside of the B() constructor.

Now that the general rule has been stated, here's the exceptions:

  1. You can safely call some function g() within a constructor if g() is a final member function, which refers to variables that have been properly assigned and final in that class level.

  2. You can safely call some function h() in a parent class, if h() is written to only mutate variables that are effectively private to the parent class.

  3. You can call some function j() in a parent class, if all the variables used by j() have a sensible assignment, but you might not see the desired results if the variables are leaked for mutation to lower sub-classes.

Basically, if you know enough about how classes are constructed, you can write your code to work with the construction order because you're programming your functions around the failure cases. The first two approaches are considered "good design" and the last approach is considered "bad design, but we do it when it works" quality code.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.