Polymorphic Construction

Heed these words of wisdom.

As a general rule in Java, never call a polymorphic method from a constructor. That is to say, never call a method that is neither static nor final, in the constructor of a class that is also not final. Because of certain ordering issues with the default construction behaviours, you can quite easily find yourself in an apparently absurd situation which is hard to understand without a lot of thought.

Take the following classes as an example:

public class A {
    public A() {
        validate();
    }

    protected void validate() {
        // Nothing to assert. Pass!
    }
}

public class B extends A {
    private final String blah = "blah";
    private final String fwee;

    public B() {
        fwee = "fwee";
    }

    @Override
    protected void validate() {
        if (!blah.equals("blah")) {
            throw new IllegalStateException();
        }
        if (!fwee.equals("fwee")) {
            throw new IllegalStateException();
        }
        // Otherwise pass!
    }
}

Can you spot the problem? No? Neither did I, at first.

If you compile these classes [zipped source, compiled jar] and construct a new B() you should receive a horrible NullPointerException from the first line of B.validate(). It turns out that when you execute a constructor such as B() the following things happen, in this order:

  1. general constructor — either super() or this() ... in this case super()
  2. default initialisation — fields that are defined outside of any method ... in this case blah = "blah"
  3. specified behaviour — the rest of the constructor ... in this case fwee = "fwee"

Because super() means A(), and A() calls validate(), we find ourself attempting to execute blah.equals("blah") before the default initialisation. So blah is null!

If you were a good (read: paranoid) programmer, you might have felt a bit nervous about assigning fwee = "fwee"; inside the constructor, since you probably realise that Java always calls another, more general, constructor (like super() or this()) at the start of every constructor — unless you explicitly call one yourself. But you, like I, may have been falsely comforted by the fact that both blah and fwee are explicitly final. Well, such comfort is false comfort, it would seem.

I don't know how to define, let alone phrase, the proper rule so I'll stick with my gross generalisation. Follow this rule, and there's one more bizarre Java bug you won't have to waste half an hour debugging in future.

Never call a polymorphic method from a constructor.

... Matty /<


Matthew Kerwin

Published
Modified
License
CC BY-SA 4.0
Tags
development, software

Comments powered by Disqus