代码之家  ›  专栏  ›  技术社区  ›  Jarek

在抽象类的构造函数中使用abstract init()函数

  •  10
  • Jarek  · 技术社区  · 15 年前

    我有这样的想法:

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

    我很好奇,当我需要创建某种结构不会及时更改的对象时,我是否会遇到这样的代码问题。

    谢谢你的回答。

    3 回复  |  直到 15 年前
        1
  •  12
  •   Community Mohan Dere    9 年前

    不要从构造函数调用可重写的方法。

    有效Java第2版,第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

    相关问题

        2
  •  2
  •   andrewmu    15 年前

    您是对的,因为它可能会导致派生类出现问题,该派生类的实例变量在构造函数中初始化,或者在创建实例时初始化。

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

    那么菜单就会 null 因为这是构造函数参数!

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

    Menu menu = new ConcreteMenu();
    menu.init();
    
        3
  •  0
  •   Eyal Schneider    15 年前

    你考虑过在构造函数中进行初始化吗?

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