为了寻求更好的编码实践,我遇到了很长一段时间以来一直在做的编码想法,甚至从这个站点上看到的程序员(我认为都是专家)都使用相同的编码想法。

例如,在下面的代码中,我扩展了Pane。为了得到班上的孩子,在这种情况下,我总是打电话给this.getChildren()。今天,我注意到Netbeans发出了警告消息,因此我进行了查找,发现要摆脱警告,我应该使用super.getChildren()。是我以后应该处理这种情况的推荐方法还是this.getChildren()一样好?

public class FlashCard extends Pane
{
    FlashCard(int num1, int num2, int answer)
    {

        Label lblNum1 = new Label(Integer.toString(num1));
        Label lblNum2 = new Label(Integer.toString(num2));
        Label lblAnswer = new Label(Integer.toString(answer));

        VBox vbox = new VBox();
        vbox.getChildren().addAll(lblNum1, lblNum2, lblAnswer);
        super.getChildren().add(vbox);
    }
}

最佳答案

这是Josh Bloch的“ Effective Java”(第二版)中的第17项:“设计和文档化继承,否则禁止继承”。 (如果您还没有的话,我强烈建议您阅读这本书。)特别是:


  类必须遵守一些其他限制以允许继承。构造函数不得调用可重写的方法。


基本问题是,如果要根据子类构造函数中执行的初始化对FlashCard进行子类化并覆盖getChildren(),则代码将中断。 FlashCard构造函数将在子类构造函数之前被调用,因此它将在初始化发生之前调用getChildren()。在一个人为的示例中:

public class SpecialFlashCard extends FlashCard {

    private ObservableList<Node> subclassChildren ;

    public SpecialFlashCard(int num1, int num2, int answer) {
        super(num1, num2, answer);
        subclassChildren = FXCollections.observableArrayList();
    }

    @Override
    public ObservableList<Node> getChildren() {
        return subclassChildren ;
    }
}


如果FlashCard构造函数调用this.getChildren(),则调用子类构造函数将抛出NullPointerException,因为超类构造函数会在初始化之前尝试向subclassChildren添加元素。另一方面,如果FlashCard构造函数调用super.getChildren(),则子类将无法按预期方式运行(因为getChildren()将返回不包含标签的列表)。

这里最简单的方法要么是禁止FlashCard的子类化,要么首先是避免子类Pane

要禁止子类化,请使FlashCard最终:

public final class FlashCard extends Pane {

    public FlashCard(int num1, int num2, int answer) {

        Label lblNum1 = new Label(Integer.toString(num1));
        Label lblNum2 = new Label(Integer.toString(num2));
        Label lblAnswer = new Label(Integer.toString(answer));

        VBox vbox = new VBox();
        vbox.getChildren().addAll(lblNum1, lblNum2, lblAnswer);
        this.getChildren().add(vbox);
    }
}


这完全避免了该问题,因为现在您不能继承FlashCard的子类,因此无法覆盖它的getChildren()方法(不再从构造函数中调用可重写的方法)。

另一种方法(我倾向于使用)不是首先将Pane子类化。这是来自有效Java的第16项:“从继承中看好构成”。

public class FlashCard {

    private final Pane pane ;

    public FlashCard(int num1, int num2, int answer) {
        Label lblNum1 = new Label(Integer.toString(num1));
        Label lblNum2 = new Label(Integer.toString(num2));
        Label lblAnswer = new Label(Integer.toString(answer));

        VBox vbox = new VBox();
        vbox.getChildren().addAll(lblNum1, lblNum2, lblAnswer);

        this.pane = new Pane();
        this.pane.getChildren().add(vbox);
    }

    public Pane asPane() {
        return pane ;
    }
}


(请注意,构造函数不会调用任何可重写的方法。)

这允许与您现有的FlashCard类基本相同的功能,但API稍有修改。例如。代替

FlashCard flashCard = new FlashCard(6, 9, 42);
someContainer.getChildren().add(flashCard);


你做

FlashCard flashCard = new FlashCard(6, 9, 42);
someContainer.getChildren().add(flashCard.asPane());

10-08 07:12