关于从构造函数调用可重写方法
简单地说,这是错误的,因为它不必要地为许多bug打开了可能性。当@Override被调用时,对象的状态可能是不一致和/或不完整的。
引用Effective Java 2nd Edition, Item 17:用于继承的设计和文档,否则禁止它:
There are a few more restrictions that a class must obey to allow inheritance. Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will be invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected.
这里有一个例子来说明:
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"
}
}
在这里,当基本构造函数调用overrideMe时,Child还没有完成最终int x的初始化,方法得到了错误的值。这几乎肯定会导致错误和错误。
相关问题
从父类构造函数调用重写方法
在Java中,基类构造函数调用重写方法时派生类对象的状态
在抽象类的构造函数中使用抽象init()函数
另请参阅
从超类的构造函数调用的字段方法的未初始化读取
有很多参数的对象构造
具有许多参数的构造函数可能导致较差的可读性,存在更好的替代方法。
下面引用Effective Java 2nd Edition, Item 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);
但是,您目前不能只设置名称和is可调,而将级别设置为默认值。您可以提供更多的构造函数重载,但显然,随着参数数量的增长,重载的数量会激增,您甚至可能有多个布尔和int参数,这真的会把事情搞得一团糟。
正如您所看到的,这不是一个令人愉快的模式,使用起来更不愉快(“true”在这里是什么意思?13是什么?)。
Bloch建议使用一个构建器模式,它允许你写这样的东西:
Telescope telly = new Telescope.Builder("X/1999").setAdjustable(true).build();
请注意,现在已对参数进行了命名,您可以按照您想要的任何顺序设置它们,并且可以跳过希望保持默认值的参数。这当然比伸缩构造函数好得多,特别是当有大量属于许多相同类型的参数时。
另请参阅
维基百科/建造者模式
Effective Java 2nd Edition,第2项:在面对许多构造函数参数时考虑一个构造器模式(在线摘录)
相关问题
什么时候使用构建器模式?
这是一个众所周知的设计模式吗?它叫什么名字?