Question

One of my classes inherits from a class in a framework I use. The superclass calls a method in its constructor which I overwrite in my own class. The method uses a field I want to initialize before it is called by the super constructor to avoid a NullPointerException.

Is there any way to do this?

Here is a synthetic test scenario, I want c in Child to not be null when call is called.

public class Test {

    public static class Parent {
        public Parent() {
            super();
            call();
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Parent");
        }
    }

    public static class Child extends Parent {

        private Child c = this;

        public Child() {
            super();
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Child, c is " + (c == null ? "null" : "this"));
        }
    }

    public static void main(String[] args) {
        new Child();
    }
}

Prior to Java 7, that was possible. I could get by with stunts like this:

    public static class Child extends Parent {

        private Child c;

        private Child(Object unused) {
            super();
        }

        public Child() {
            this(c = this);
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Child, c is " + (c == null ? "null" : "this"));
        }
    }

Now, that won't work anymore. I appreciate the additional safety, but the call from super destroys any safety gained by it and reduces the flexibility.

I'd like a way to circumvent this restriction. As an alternative, I'd like to know what's gained by an restriction that spares the super constructor case.

Was it helpful?

Solution 2

This is an answer to the "I'd like to know what's gained by an restriction that spares the super constructor case." part of the question.

In the course of construction, there are three states the fields declared in a class X might be in: All default values, all fully initialized to consistent working values, and anything else.

The objective seems to be that code in classes other than X only sees one of the first two states. When non-static initializer or constructor code for any of X's superclasses is running, X's fields are all in the default state. When non-static initializer or constructor code for any subclass of X is running, all X's fields have been initialized to a fully consistent, usable state.

Only X initializer and constructor code should have to deal with X fields in an inconsistent state, some initialized, some default, and some partially initialized.

This can certainly be circumvented by calling X methods from an X superclass initializer or constructor, but that is commonly regarded as an anti-pattern. The problem is running X code that is not called locally from an initializer or constructor in a partially constructed X. If that code changes a field, the change may be overwritten when the X initializers and constructor body run.

OTHER TIPS

A static initializer will be called before the super class constructor. However, you won't be able to set any non-static fields, so it most likely won't help.

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

A non-static initialization block also doesn't help as it is called after the super class constructor completes.

Another approach may be to do nothing when called from the super-constructor and make the call again the child constructor, e.g:

    public Child() {
        super();
        call();
    }

    public void call() {

       if (c==null) {
         return;
       }

       System.out.println("do something with c now");

    }

This won't work if more stuff happens in the super constructor that is dependent on this method though.

I have to agree with EJP that this is all a bad idea; it would be much better to find a completely different solution that doesn't involve torturing constructors.

Note that your class Child is translated into the following equivalent by the Java compiler:

public static class Child extends Parent {

    private Child c;

    public Child() {
        super();
        c = this;
    }

    // Remaining implementation
}

This is the same for Java 6 and 7, the generated byte code for the constructor is even the same when compiling with any of both versions. The local field is always instantiated after calling the super constructor. What compiler did you use to make your "work-around" work?

This restriction is quite elementary. This way, you can rely on that super constructors are applied first. Imagine, your sub constructor was using a final field declared in this class. You could not guarantee that this field was initialized if you would not guarantee this constructor execution order. This restriction makes Java more reliable.

This should never have worked in the first place.

Note that at the bytecode level, this actually is allowed. In bytecode, you can set fields declared in the current class before calling a superclass constructor. However, Java provides no way to use this behavior. It is only used secretly by the Java compiler to initialize the synthetic fields added to support inner classes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top