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

Danylo Fitel picture Danylo Fitel · Mar 10, 2013 · Viewed 15k times · Source

Let's suppose I have an abstract Base class that implements Runnable interface.

public abstract class Base implements Runnable {

  protected int param;

  public Base(final int param) {
      System.out.println("Base constructor");
      this.param = param;
      // I'm using this param here
      new Thread(this).start();
      System.out.println("Derivative thread created with param " + param);
  }

  @Override
  abstract public void run();
}

And here is one of a few derivative classes.

public class Derivative extends Base {

  public Derivative(final int param) {
      super(param);
  }

  @Override
  public void run() {
      System.out.println("Derivative is running with param " + param);
  }

  public static void main(String[] args) {
      Derivative thread = new Derivative(1);
  }

}

The point is that I want my Base class do some general stuff instead of copying it every time. Actually, it's running fine, the output is always the same:

Base constructor Derivative thread created with param 1 Derivative is running with param 1

But is it safe IN JAVA to start a thread calling the abstract method in constructor? Because, in C++ and C# it is unsafe in most cases, so far as I know. Thank you!

Answer

Ryan Stewart picture Ryan Stewart · Mar 10, 2013

This code demonstrates why you should never call an abstract method, or any other overridable method, from a constructor:

abstract class Super {
    Super() {
        doSubStuff();
    }
    abstract void doSubStuff();
}

class Sub extends Super {
    String s = "Hello world";

    void doSubStuff() {
        System.out.println(s);
    }
}

public static void main(String[] args) {
    new Sub();
}

When run, this prints null. This means the only "safe" methods to have in a constructor are private and/or final ones.

On the other hand, your code doesn't actually call an abstract method from a constructor. Instead, you pass an uninitialized object to another thread for processing, which is worse, since the thread you're starting may be given priority and execute before your Base finishes its initialization.