是否可以在Java中从构造函数调用抽象方法?

2022-09-01 17:12:17

假设我有一个实现 Runnable 接口的抽象基类。

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();
}

这是为数不多的衍生类之一。

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);
  }

}

关键是我希望我的基类做一些常规的事情,而不是每次都复制它。实际上,它运行良好,输出始终相同:

使用参数 1 创建的基构造函数导数线程 正在使用参数 1 运行

但是,在JAVA中启动一个在构造函数中调用抽象方法的线程是否安全?因为,据我所知,在C++和C#中,在大多数情况下它是不安全的。谢谢!


答案 1

此代码演示了为什么永远不要从构造函数调用抽象方法或任何其他可重写的方法:

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();
}

运行时,这将打印 。这意味着构造函数中唯一的“安全”方法是私有和/或最终方法。null

另一方面,您的代码实际上并没有从构造函数调用抽象方法。相反,您将一个未初始化的对象传递给另一个线程进行处理,这更糟,因为您正在启动的线程可能会被赋予优先级并在完成其初始化之前执行。Base


答案 2

这不是一个好主意,因为当调用 run() 时,派生对象可能尚未初始化。如果 run() 依赖于 Derivative 中的任何状态,它可能会失败。

在您的简单案例中,它有效。但是,子类就没有意义了。您可以简单地

public Base(final int param, Runnable action) {

  new Thread(action).start();