从构造函数调用可重写方法
简而言之,这是错误的,因为它不必要地开启了以下可能性:MANY错误。当。。。的时候@Override
被调用时,对象的状态可能不一致和/或不完整。
引用自《Effective Java》第二版,第 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
,并且该方法得到错误的值。这几乎肯定会导致错误和错误。
相关问题
- 从父类构造函数调用重写的方法 https://stackoverflow.com/questions/2898422/calling-an-overridden-method-from-a-parent-class-constructor
- 当基类构造函数调用Java中重写方法时派生类对象的状态 https://stackoverflow.com/questions/3330390/state-of-derived-class-object-when-base-class-constructor-calls-overridden-method
- 在抽象类的构造函数中使用抽象 init() 函数 https://stackoverflow.com/questions/3342784/using-abstract-init-function-in-abstract-classs-constructor
See also
- FindBugs - 从超类的构造函数调用的字段方法的未初始化读取 http://findbugs.sourceforge.net/bugDescriptions.html#UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR
关于具有多个参数的对象构造
具有许多参数的构造函数可能会导致可读性较差,但存在更好的替代方案。
这是引用自《Effective Java》第二版,第 2 条:面对许多构造函数参数时考虑构建器模式:
传统上,程序员使用伸缩构造函数模式,其中您提供一个仅具有必需参数的构造函数,另一个具有单个可选参数,第三个具有两个可选参数,依此类推...
伸缩构造函数模式本质上是这样的:
public class Telescope {
final String name;
final int levels;
final boolean isAdjustable;
public Telescope(String name) {
this(name, 5);
}
public Telescope(String name, int levels) {
this(name, levels, false);
}
public Telescope(String name, int levels, boolean isAdjustable) {
this.name = name;
this.levels = levels;
this.isAdjustable = isAdjustable;
}
}
现在您可以执行以下任一操作:
new Telescope("X/1999");
new Telescope("X/1999", 13);
new Telescope("X/1999", 13, true);
但是,目前您不能仅设置name
and isAdjustable
,并离开levels
默认情况下。你可以提供更多的构造函数重载,但显然这个数字会随着参数数量的增长而爆炸,甚至可能有多个boolean
and int
争论,这真的会让事情变得一团糟。
正如您所看到的,这不是一个写起来令人愉快的模式,使用起来更不愉快(“true”在这里意味着什么?13 是什么?)。
Bloch 建议使用构建器模式,它允许您编写如下所示的内容:
Telescope telly = new Telescope.Builder("X/1999").setAdjustable(true).build();
请注意,现在参数已命名,您可以按所需的任何顺序设置它们,并且可以跳过要保留默认值的参数。这肯定比伸缩构造函数好得多,特别是当存在大量属于许多相同类型的参数时。
See also
- 维基百科/构建器模式 http://en.wikipedia.org/wiki/Builder_pattern
-
《Effective Java》第二版,第 2 条:面对许多构造函数参数时考虑构建器模式 (网上摘录 http://www.codeproject.com/KB/books/EffectiveJava.aspx)
相关问题
- 你什么时候会使用建造者模式? https://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern
- 这是众所周知的设计模式吗?它叫什么名字? https://stackoverflow.com/questions/2637268/is-this-a-well-known-design-pattern-what-is-its-name/