我有一个类,其构造函数如下所示:
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
(以及其他面板)的代码不会因此更改而变得混乱。任何帮助非常感谢。感谢。
答案 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
吗?每个类都不能在自己的构造函数中进行自己的初始化,而不依赖于其子类状态的任何内容吗?