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

mis*_*tor 1 java oop swing 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
}
Run Code Online (Sandbox Code Playgroud)

在构造函数中,首先初始化要使用传递给构造函数的值进行初始化的字段.然后按顺序调用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
} 
Run Code Online (Sandbox Code Playgroud)

initializeComponents这里需要的方法b,遗憾的是在那时没有初始化.

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

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

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

JB *_*zet 7

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

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

FooPanel p = new FooPanel(a, b);
p.initialize();
Run Code Online (Sandbox Code Playgroud)

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

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


Jon*_*eet 5

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

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

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