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

☆樱花仙子☆ 提交于 2019-12-20 03:26:46

问题


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)?


回答1:


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.




回答2:


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 (i.e. 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.




回答3:


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.



来源:https://stackoverflow.com/questions/11261197/can-a-java-subclasss-private-final-field-be-initialized-before-the-super-constr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!