Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

In this short tutorial, we'll show how we can get the error Cannot reference “X” before supertype constructor has been called, and how to avoid it.

2. Constructors Chain

A constructor can call exactly one other constructor. This call must be in the first line of its body.

We can call a constructor of the same class with the keyword this, or we can call a constructor of the superclass with the keyword super.

When a constructor doesn't call another constructor, the compiler adds a call to the no-argument constructor of the superclass.

3. Our Compilation error

This error boils down to trying to access instance level members before we invoke the constructor chain.

Let's see a couple of ways we might run into this.

3.1. Referring To An Instance Method

In the next example, we'll see the compilation error Cannot reference “X” before supertype constructor has been called at line 5. Note that constructor attempts to use the instance method getErrorCode() too early:

public class MyException extends RuntimeException {
    private int errorCode = 0;
    
    public MyException(String message) {
        super(message + getErrorCode()); // compilation error
    }

    public int getErrorCode() {
        return errorCode;
    }
}

This errors because, until super() has completed, there isn't an instance of the class MyException. Therefore, we can't yet make our call to the instance method getErrorCode().

3.2. Referring To An Instance Field

In the next example, we see our exception with an instance field instead of an instance method. Let's take a look at how the first constructor tries to use an instance member before the instance itself is ready:

public class MyClass {

    private int myField1 = 10;
    private int myField2;

    public MyClass() {
        this(myField1); // compilation error
    }

    public MyClass(int i) {
        myField2 = i;
    }
}

A reference to an instance field can only be made after its class has been initialized, meaning after any call to this() or super().

So, why is there no compiler error in the second constructor, which also uses an instance field?

Remember that all classes are implicitly derived from class Object, and so there is an implicit super() call added by the compiler:

public MyClass(int i) {
    super(); // added by compiler
    myField2 = i;
}

Here, Object‘s constructor gets called before we access myField2, meaning we're okay.

4. Solutions

The first possible solution to this problem is trivial: we don't call the second constructor. We do explicitly in the first constructor what we wanted to do in the second constructor.

In this case, we'd copy the value of myField1 into myField2:

public class MyClass {

    private int myField1 = 10;
    private int myField2;

    public MyClass() {
        myField2 = myField1;
    }

    public MyClass(int i) {
        myField2 = i;
    }
}

In general, though, we probably need to rethink the structure of what we're building.

But, if we're calling the second constructor for a good reason, for example, to avoid repeating code, we can move the code into a method:

public class MyClass {

    private int myField1 = 10;
    private int myField2;

    public MyClass() {
        setupMyFields(myField1);
    }

    public MyClass(int i) {
        setupMyFields(i);
    }

    private void setupMyFields(int i) {
        myField2 = i;
    }
}

Again, this works because the compiler has implicitly called the constructor chain before invoking the method.

A third solution could be that we use static fields or methods. If we change myField1 to a static constant, then the compiler is also happy:

public class MyClass {

    private static final int SOME_CONSTANT = 10;
    private int myField2;

    public MyClass() {
        this(SOME_CONSTANT);
    }

    public MyClass(int i) {
        myField2 = i;
    }
}

We should note that making a field static means that it becomes shared with all the instances of this object, so it's not a change to make too lightly.

For static to be the right answer, we need a strong reason. For example, maybe the value is not really a field, but instead a constant, so it makes sense to make it static and final. Maybe the construction method we wanted to call doesn't need access to the instance members of the class, meaning it should be static.

5. Conclusion

We saw in this article how making a reference to instance members before the super() or this() call gives a compilation error. We saw this happen with an explicitly declared base class and also with the implicit Object base class.

We also demonstrated that this is an issue with the design of the constructor and showed how this can be fixed by repeating code in the constructor, delegating to a post-construction setup method, or the use of constant values or static methods to help with construction.

As always the source code for this example can be found over on GitHub.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!