如何在特定情况下处理变量的初始化

时间:2012-06-01 12:12:27

标签: java swing oop refactoring initialization

我有一个类,其构造函数如下所示:

abstract class BasePanel extends JPanel {
  public BasePanel(A a) {
    // initializing fields from values passed to ctor
    this.a = a;
    // initializing gui components
    initializeComponents();
    setupPanels();
    concludeUiSetup();
  }

  // stuff
}

在构造函数中,首先初始化要使用传递给构造函数的值进行初始化的字段。然后按顺序调用UI设置所需的其他方法。需要在子类中重写其中两个方法,以便针对它们进行UI设置。

现在考虑扩展FooPanel的课程BasePanel。它在构造函数中需要更多的初始化参数。

class FooPanel extends BasePanel {
  public FooPanel(A a, B b) {
    super(a);
    this.b = b;
  }

  @Override
  public void initializeComponents() {
    super.initializeComponents();
    // I require b here, but oops, b is not initialized at this point, and so 
    // this will throw NPE.
    someTextField.setText(b.get());
  }

  // stuff
} 
这里的

initializeComponents方法需要b,遗憾的是此时尚未初始化。

重构此代码的适当方法是:

  • 需要的字段在需要之前设置。
  • 使用FooPanel(以及其他面板)的代码不会因此更改而变得混乱。

任何帮助非常感谢。感谢。

2 个答案:

答案 0 :(得分:7)

您不应该从构造函数中调用可覆盖的方法。在这种情况下,您应该做的是定义一个只初始化实例字段的构造函数,并将GUI的初始化放在一个可重写的initialize()方法中,该方法不是从构造函数中调用的。

因此,要构建一个FooPanel,你可以:

FooPanel p = new FooPanel(a, b);
p.initialize();

如果您不强制FooPanel的所有客户端执行此操作,则将构造函数定义为private,并提供工厂方法:

public static FooPanel create(A a, B b) {
    FooPanel p = new FooPanel(a, b);
    p.initialize();
    return p;
}

答案 1 :(得分:5)

基本上,尽量避免在构造函数中调用虚拟(即可覆盖)方法。它确实导致了这种问题。如果你要在构造函数中调用虚方法,你需要记录它 - 并且很可能避免在其他任何地方调用它。必须编写这样的方法来处理尚未完全初始化的对象,这使得它处于一个尴尬的位置。

在没有更多信息的情况下很难知道更具体的建议,但我也鼓励你尽可能地接受组合而不是继承 - 或者至少总是考虑它,并决定最优雅的方法。

如果你真的想要继承,你真的需要initializeComponents吗?每个类都不能在自己的构造函数中进行自己的初始化,而不依赖于其子类状态的任何内容吗?