Использование абстрактной функции init() в конструкторе абстрактного класса
У меня есть что-то вроде этого:
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 суперкласса является абстрактным и вызывается конструктором автоматически после создания объекта.
Мне любопытно, могу ли я столкнуться с какими-то проблемами с кодом, подобным этому, когда мне нужно создать какой-то объект такого рода, структура которого не будет изменена во времени.
Будет ли какой-нибудь подход лучше? Он работает в Java, но будет ли он работать в C++ и, возможно, ActionScript?
Спасибо за ответ.
3 ответа
НЕ ИСПОЛЬЗУЙТЕ ПЕРЕОБРАЗУЕМЫЕ МЕТОДЫ ИЗ СТРОИТЕЛЬСТВА.
Цитата из Effective Java 2nd Edition, Item 17: Разработка и документация для наследования, или же запретить это:
Есть еще несколько ограничений, которым должен следовать класс, чтобы разрешить наследование. Конструкторы не должны вызывать переопределяемые методы, прямо или косвенно. Если вы нарушите это правило, произойдет сбой программы. Конструктор суперкласса выполняется перед конструктором подкласса, поэтому метод переопределения в подклассе будет вызван до запуска конструктора подкласса. Если переопределяющий метод зависит от какой-либо инициализации, выполняемой конструктором подкласса, метод не будет работать должным образом.
Вот пример для иллюстрации:
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
и метод получает неправильное значение. Это почти наверняка приведет к ошибкам и ошибкам.
Смежные вопросы
- Вызов переопределенного метода из конструктора родительского класса
- Состояние объекта производного класса, когда конструктор базового класса вызывает переопределенный метод в Java
Смотрите также
Вы правы в том, что это может вызвать проблемы с производным классом, чьи переменные экземпляра инициализируются в конструкторе или при создании экземпляра. Если у вас было это:
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"));
// ....
}
}