Подтвердить что ты не робот

Можно ли вызвать абстрактный метод из конструктора в Java?

Предположим, что у меня есть абстрактный базовый класс, реализующий интерфейс 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 запустить поток, вызывающий абстрактный метод в конструкторе? Потому что, в С++ и С#, это небезопасно в большинстве случаев, насколько я знаю. Спасибо!

4b9b3361

Ответ 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

Это очень плохая практика для вызова абстрактного метода из конструктора. Методы, вызываемые из конструкторов, всегда должны быть закрытыми или окончательными, чтобы предотвратить переопределение.

Смотрите эту ссылку на вопрос здесь

Ответ 3

Не очень хорошая идея, так как при вызове run() объект Derivative может не быть инициализирован. Если run() зависит от любого состояния в производном, он может выйти из строя.

В вашем простом случае это работает. Но тогда нет никакого смысла для подкласса. Вы можете просто

public Base(final int param, Runnable action) {

  new Thread(action).start();

Ответ 4

Передача this из конструктора называется "позволяя this выйти из конструктора" и может привести к некоторым особенно неприятным и странным ошибкам, поскольку объект может находиться в несогласованном состоянии.

Это особенно относится к случаю, когда this передается в другой поток, как в этом примере. Из-за того, что JVMs имеет право переупорядочивать утверждения в потоке, вы можете получить поведение undefined поведение/состояние.