简体   繁体   中英

JAVA inheritance constructor order of call

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 Af() (thru the automatic call to A 's constructor) but looks like its calling Bf()

Why?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM