我知道Java对象构造函数隐式初始化它们的实例的非静态字段。但是,我不确定在类层次结构中发生这种情况的顺序。例如:
abstract public class AbstractPieceSequence implements PieceSequence
{
private Tetromino current;
private Tetromino preview;
public AbstractPieceSequence()
{
advance();
}
@Override
public final void advance()
{
if (preview == null) {
current = getNextPiece();
preview = getNextPiece();
} else {
current = preview;
preview = getNextPiece();
}
}
abstract protected Tetromino getNextPiece();
}
public class ShufflePieceSequence extends AbstractPieceSequence
{
private List<Shape> bag = new LinkedList<Shape>();
@Override
protected Tetromino getNextPiece()
{
if (bag.size() == 0) {
Collections.addAll(bag, Shape.I, Shape.J, Shape.L, Shape.O, Shape.S, Shape.T, Shape.Z);
}
return Tetromino.tetrominoes.get(bag.remove(0));
}
}
父类的构造函数调用子类中的一个方法,该方法抛出异常,因为List<Shape> bag
的值当前为null。
我可以定义一个子构造函数并调用super(),但这必须是构造函数体中的第一行(这意味着我仍然没有机会在调用getNextPiece
之前初始化bag)。
我遗漏了一些明显的东西。
答案 0 :(得分:15)
没错。 super()
,即使你没有明确地添加它,也会被隐含地放在每个构造函数中。这意味着首先调用ShufflePieceSequence
的构造函数,但它所做的就是调用AbstractPieceSequence
。
在AbstractPieceSequence
中,您正在调用ShufflePieceSequence
中定义的方法 - 该方法尚未初始化。事实上,你所做的实际上是一个非常微妙的bug 。你永远不应该从构造函数调用overridable(包括abstract
方法)。期。像pmd和findbugs这样的AFAIR工具将此标记为潜在错误。
答案 1 :(得分:4)
对象字段未被隐式初始化...您需要执行init。也许在这种情况下你需要一个懒惰的初始化?让构造函数调用方法做一些非常重要的工作通常很不愉快,通常是一种比想要的更复杂的气味。
答案 2 :(得分:3)
深度优先,预订步行。
Anders提出了一个很好的观点:Java只隐式初始化本机类型的字段。任何Object字段只是对Object的引用,因此它实际上已初始化,但它已初始化为null
。
答案 3 :(得分:0)
在继承的情况下调用Parent-Sub类的构造函数的顺序是,Parent类的构造函数总是首先调用,然后是Child类的构造函数。
如果没有给出,Sub类默认使用Super()调用基类的构造函数。