Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java, is there a legitimate reason to call a non-final method from a class constructor?

I recently spent quite a few minutes debugging a problem in production code that in the end turned out to be caused by a class calling an abstract method in its constructor, and the subclass implementation of that method tried to use a subclass field that had not been initialized yet (An example that illustrates the point is included below)

While researching this, I stumbled across this question, and was intrigued by Jon Skeet's answer:

In general it's a bad idea to call a non-final method within a constructor for precisely this reason - the subclass constructor body won't have been executed yet, so you're effectively calling a method in an environment which hasn't been fully initialized.

This has me wondering, is there ever a legitimate reason to call a non-final or abstract method from a constructor? Or is it pretty much always a sign of bad design?

Example

public class SSCCE {
    static abstract class A {
        public A() {
            method();  // Not good; field arr in B will be null at this point!
        }

        abstract void method();
    }

    static class B extends A {
        final String[] arr = new String[] { "foo", "bar" };

        public B() {
            super();
            System.out.println("In B(): " + Arrays.toString(arr));
        }

        void method() {
            System.out.println("In method(): " + Arrays.toString(arr));
        }
    }

    public static void main(String[] args) {
        new B().method();
    }
}

And here is expected output:

In method(): null
In B(): [foo, bar]
In method(): [foo, bar]

The problem, of course, is that in the first call to method() the field arr is null because it hasn't been initialized yet.

like image 942
Kevin K Avatar asked Sep 19 '11 21:09

Kevin K


People also ask

Can we call a method from constructor in Java?

No, you cannot call a constructor from a method. The only place from which you can invoke constructors using “this()” or, “super()” is the first line of another constructor.

Is it OK to call abstract method from constructor in Java?

It's a very bad practice to call an abstract method from a constructor. Methods called from constructors should always be private or final, to prevent overriding.

What is non final method Java?

If a constructor calls a method that is overridden in a subclass, it can cause the overriding method in the subclass to be called before the subclass has been initialized. This can lead to unexpected results.

Can final class have non final methods?

All methods in a final class are implicitly final. To be pedantic, whether or not the methods are implicitly final is moot; there is no opportunity to attempt to override them!


2 Answers

There are times it can be very hard not to.

Take Joda Time, for example. Its Chronology type hierarchy is very deep, but the abstract AssembledChronology class is based on the idea that you assemble a bunch of "fields" (month-of-year etc). There's a non-final method, assembleFields, which is called during the constructor, in order to assemble the fields for that instance.

They can't be passed up the constructor chain, because some of the fields need to refer back to the chronology which creates them, later on - and you can't use this in a chained constructor argument.

I've gone to nasty lengths in Noda Time to avoid it actually being a virtual method call - but it's something remarkably similar, to be honest.

It's a good idea to avoid this sort of thing if you possibly can... but sometimes it's a real pain in the neck to do so, especially if you want your type to be immutable after construction.

like image 192
Jon Skeet Avatar answered Sep 27 '22 21:09

Jon Skeet


One example is the non-final (and package-private) method HashMap#init(), an empty method which is in place for the exact purpose of being overriden by subclasses:

/**
 * Initialization hook for subclasses. This method is called
 * in all constructors and pseudo-constructors (clone, readObject)
 * after HashMap has been initialized but before any entries have
 * been inserted.  (In the absence of this method, readObject would
 * require explicit knowledge of subclasses.)
 */
void init() {
}

(from the HashMap source)

I don't have any examples of how it's used by subclasses - if anyone does, feel free to edit my answer.

EDIT: To respond to @John B's comment, I'm not saying it must be good design since it's used in the source. I just wanted to point out an example. I do notice that each HashMap constructor takes care to call init() last, but this is of course still before the subclass constructor. So an amount of responsibility is falling to the subclass implementation not to muck things up.

like image 29
Paul Bellora Avatar answered Sep 27 '22 22:09

Paul Bellora