在抽象类的构造函数中使用抽象的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中使用吗?

谢谢你的答案。

不要从构造函数中发现过多的方法。

引用Effective Java第2版,第17项:设计和inheritance文档,或者禁止它

为了允许inheritance,类必须遵守一些限制。 构造函数不得直接或间接调用可覆盖的方法 。 如果违反此规则,将导致程序失败。 超类构造函数在子类构造函数之前运行,因此在子类构造函数运行之前将调用子类中的重写方法。 如果重写方法依赖于子类构造函数执行的任何初始化,则该方法将不会按预期运行。

这是一个例子来说明:

 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构造函数调用overrideMeChild尚未完成初始化final int x ,并且该方法获取了错误的值。 这几乎肯定会导致错误和错误。

相关问题

  • 从父类构造函数调用重写方法
  • 当Base类构造函数在Java中调用重写方法时,Derived类对象的状态

也可以看看

  • FindBugs – 从超类的构造函数调用的字段方法的未初始化读取

你是对的,它可能会导致派生类出现问题,派生类的实例变量在构造函数中初始化或者在创建实例时。 如果你有这个:

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

然后MenuItem将为null因为它是构造函数参数!

在构造函数中调用非final方法是一种冒险的做法。

一个简单的解决方案可能是分离构造和初始化,如下所示:

 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")); // .... } }