Использование абстрактной функции init () в конструкторе абстрактного classа

У меня есть что-то вроде этого:

public abstract class Menu { public Menu() { init(); } protected abstract void init(); protected void addMenuItem(MenuItem menuItem) { // some code... } } public class ConcreteMenu extends Menu { protected void init() { addMenuItem(new MenuItem("ITEM1")); addMenuItem(new MenuItem("ITEM2")); // .... } } //Somewhere in code Menu menu1 = new ConcreteMenu(); 

Как вы видите, метод init суперclassа является абстрактным и автоматически вызывается конструктором после создания объекта.

Мне любопытно, могу ли я столкнуться с некоторыми проблемами с таким кодом, когда мне нужно создать какой-то объект такого типа, структура которого не будет изменена во времени.

Будет ли какой-нибудь подход лучше? Он работает на Java, но будет ли он работать на C ++ и, возможно, в ActionScript?

Спасибо за ответ.

НЕ ПРИНИМАЙТЕ ОТКРЫТЫЕ МЕТОДЫ ОТ КОНСТРУКТОРА.

Цитата из Effective Java 2nd Edition, пункт 17: дизайн и документ для наследования, а также запрет :

Есть еще несколько ограничений, которые class должен соблюдать, чтобы разрешить наследование. Конструкторы не должны ссылаться на переопределяемые методы , прямо или косвенно. Если вы нарушите это правило, произойдет сбой программы. Конструктор суперclassа выполняется перед конструктором подclassа, поэтому метод переопределения в подclassе будет вызываться до запуска конструктора подclassа. Если метод переопределения зависит от любой инициализации, выполняемой конструктором подclassа, метод не будет вести себя так, как ожидалось.

Вот пример для иллюстрации:

 public class ConstructorCallsOverride { public static void main(String[] args) { abstract class Base { Base() { overrideMe(); } abstract void overrideMe(); } class Child extends Base { final int x; Child(int x) { this.x = x; } @Override void overrideMe() { System.out.println(x); } } new Child(42); // prints "0" } } 

Здесь, когда Base конструктор вызывает overrideMe , Child не завершил инициализацию final int x , и метод получил неправильное значение. Это почти наверняка приведет к ошибкам и ошибкам.

Связанные вопросы

  • Вызов переопределенного метода из конструктора родительского classа
  • Состояние объекта classа Derived, когда конструктор базового classа вызывает переопределенный метод в Java

Смотрите также

  • FindBugs – Неинициализированное чтение метода поля, вызванного из конструктора суперclassа

Вы правы в том, что это может вызвать проблемы с производным classом, переменные экземпляра которого инициализируются в конструкторе или при создании экземпляра. Если у вас есть это:

 public class ConcreteMenu extends Menu { String firstItem = "Item1"; protected void init() { addMenuItem(new MenuItem(firstItem)); // .... } } 

Тогда MenuItem будет иметь null поскольку это аргумент конструктора!

Вызов не конечных методов в конструкторах – это рискованная практика.

Простым решением может быть разделение конструкции и инициализации, например:

 Menu menu = new ConcreteMenu(); menu.init(); 

Как упоминалось выше, вызов переопределяемого метода из конструктора входит в мир боли …

Рассматривали ли вы выполнение инициализации в самом конструкторе?

 public abstract class Menu { public Menu() { .... } protected void addMenuItem(MenuItem menuItem) { // some code... } } public class ConcreteMenu extends Menu { public ConcreteMenu() { super(); addMenuItem(new MenuItem("ITEM1")); addMenuItem(new MenuItem("ITEM2")); // .... } }