我有这种继承结构:
public abstract class Mom {
int dummy;
Mom() {
dummy = 0;
}
Mom(int d) {
this();
dummy = d;
}
}
public class Kid extends Mom {
String foo;
Kid() {
super();
foo = "";
}
Kid(int d) {
super(d);
}
}
// ...
Kid kiddo = new Kid(10);
// kiddo.foo == null !
Kid
的无参数构造函数从未被调用过!这就是我的预期:
new Kid(10)
→Kid#Kid(int)
super(d)
→Mom#Mom(int)
this()
→Kid#Kid()
// doh !! super()
→Mom#Mom()
是否可以从Mom
调用Kid
的无参数构造函数?
我想这不是,我会添加一个init()
将Mom
调用的抽象方法[1] Kid
,super()
将 覆盖。<登记/>
但我只是想知道确切的原因,如果可能的话,证明为什么要调用子类'构造函数的例子是个坏主意(即使子类'构造函数 调用// in Mom:
protected abstract void init();
public Mom() {
dummy = 0;
init();
}
// in Kid:
@Override
protected abstract void init() {
foo = "";
}
)。
{{1}}
答案 0 :(得分:6)
我安排这些的方式,所以你不需要调用每个构造函数。
public abstract class Parent {
final int dummy;
Parent () {
this(0);
}
Parent (int d) {
dummy = d;
}
}
public class Kid extends Parent {
final String foo = "";
Kid() {
}
Kid(int d) {
super(d);
}
}
使用final
可确保每个字段都设置一次。
从构造函数中调用任何可覆盖的方法被认为是不好的做法,因此使构造函数覆盖是一个坏主意。
this()
调用相同类的构造函数,因为构造函数不遵循继承(也不执行静态方法)
new Kid(10) --> Kid#Kid(int)
super(d) --> Mom#Mom(int)
this() --> Mom#Mom()
构造函数执行此操作,否则您将面临多次调用同一构造函数的危险,并且无法保证最终方法只设置一次。
答案 1 :(得分:2)
来自JLS §8.8.7.1(我强调):
备用构造函数调用以关键字
this
开头(可能以显式类型参数开头)。 他们习惯了 调用同一类的替代构造函数。超类构造函数调用以关键字
super
(可能以显式类型参数开头)或主要关键字开头 表达。 它们用于调用direct的构造函数 超类。强>
因此,this-constructor-invocation总是引用同一个类,从不引用子类。
虽然可以在构造函数中调用虚方法,但它不安全并且被认为是不好的做法,因为它可能导致这些方法使用部分初始化的对象实例。
对于您的问题,有几种可能的解决方案:
foo = "";
。这也称为字段初始值设定项 { foo = ""; }
。请注意,如果您的班级需要,您可以拥有多个实例初始化程序。根据JLS §12.5,(1)和(2)中的初始化总是在构造函数本身被调用之前执行,所以你有一个明确定义的对象初始化而不需要求助有问题的模式。
如果成员多次初始化,则最后一次初始化获胜:
4)执行实例初始值设定项和实例变量初始值设定项 对于此类,分配实例变量初始值设定项的值 到相应的实例变量,按从左到右的顺序 它们以文本形式出现在该类的源代码中。
如果在构造函数中初始化相同的字段,则构造函数将获胜。
答案 2 :(得分:0)
你应该为Kid类使用这些构造函数:
Kid(int i) {
super(i);
whatever();
}
Kid () {
this( DEFAULT_VALUE);
}
这样所有对父构造函数的调用都是通过子类的完全限定构造函数来完成的。并且对于您的类的所有构造函数都有一个默认行为,就像当前代码一样,它没有被绕过。