Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the super constructor have to be called before fields can be accessed?

Tags:

java

bytecode

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.

like image 494
Arne Avatar asked Jan 11 '23 18:01

Arne


1 Answers

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.

like image 119
Adrian Mouat Avatar answered Feb 03 '23 12:02

Adrian Mouat