Cannot Reference “X” Before Supertype Constructor Has Been Called

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
.

Leave a Reply

Your email address will not be published.