1

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

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这里的方法 requires b,不幸的是当时没有初始化。

重组此代码的适当方法是什么,以便:

  • 需要的字段是在需要之前设置的。
  • 使用FooPanel(和其他面板)的代码并没有因为这种变化而变得杂乱无章。

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

4

2 回答 2

7

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

因此,要构建 FooPanel,您可以:

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

如果您不强制 FooPanel 的所有客户端都这样做,您可以将构造函数定义为私有,并提供一个工厂方法:

public static FooPanel create(A a, B b) {
    FooPanel p = new FooPanel(a, b);
    p.initialize();
    return p;
}
于 2012-06-01T12:29:17.370 回答
5

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

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

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

于 2012-06-01T12:27:16.317 回答