简体   繁体   中英

Can a java subclass's private final field be initialized before the super constructor completes?

I have a pair of classes looking like this;

public abstract class Class1 {

//...

    public Class1() {
        //...
        function2();
        //...
    }

    protected abstract void function2();

}

public class Class2 implements Class1 {

    private final OnSomethingListener mOnSomethingListener = new OnSomethingListener() {
        @Override
        onSomething() {
            doThatOtherThing();
        }
    }

    protected void function2() {
        //uses mOnSomethingListener
        //however mOnSomethingListener is null when this function is called from super()
        //...
    }

    public Class2() {
        super();
    }
}

I assume the listener is null because I am effectively referencing it from super() and it hasn't instantiated yet. However, I want to make it final because, well, it is. Can I get this field (the listener) to initialize in time without putting it in the superclass (which won't be using the listener, ever)?

Your design is an instance of the "leaked this problem" and is an anti-pattern in Java. You should never call out to a publicly overridable method from a constructor.

Short answer - no. A superclass' constructor, field initialisers and instance initialisers are always called before the subclass'.

The order of calls is formally defined in section 8.8.7.1 of the JLS . Summarising the relevant last parts (where S is the superclass and C is the subclass):

After determining the immediately enclosing instance of i with respect to S (if any), evaluation of the superclass constructor invocation statement proceeds by evaluating the arguments to the constructor, left-to-right, as in an ordinary method invocation; and then invoking the constructor.

Finally, if the superclass constructor invocation statement completes normally, then all instance variable initializers of C and all instance initializers of C are executed. If an instance initializer or instance variable initializer I textually precedes another instance initializer or instance variable initializer J, then I is executed before J.

So when the superclass constructor runs, the subclass and all its fields are completely uninitialised. It's bad practice to call overridden methods from a constructor for this reason. You're essentially letting a reference to the object "escape" from its constructor, which means all the guarantees of construction are off (including things like final fields changing value, etc.).

Calling an abstract method from a constructor is almost always the wrong thing to do. Depending on the implementation of the method in the subclass(es) you might get away with it in some case (ie if the method does not depend on any state at all), but it will almost certainly cause a hard-to-debug failure at some point.

For example, would you expect there to be a difference between:

protected String function2() {
    return "foo";
}

and

private final String foo = "foo";

protected String function2() {
    return foo;
}

Analysing problems like this is hard, because they break the mental model of how classes work. Best to avoid this situation altogether.

super classes are always initialised before subclasses. Only static fields of a sub class can be initialised first.

You can call an overridden method from the super class which then accesses a field which is not initialised. This is considered bad practice.

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