在JavaFx中处理和扩展Node的正确方法

时间:2017-05-09 18:41:19

标签: java javafx

在我寻求更好的编码实践的过程中,我遇到了一个我已经做了很长时间的编码思想,我甚至看到来自这个网站的程序员,我认为是专家,使用相同的编码思想。

例如,在下面的代码中,我扩展了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);
    }
}

1 个答案:

答案 0 :(得分:3)

这是Josh Bloch的第17项" Effective Java" (第二版):"继承的设计和文档,否则禁止它&#34 ;。 (我强烈建议您阅读本书,如果您还没有。)具体来说:

  

为了允许继承,类必须遵守一些限制。 构造函数不得调用可覆盖的方法。

基本问题是,如果您要以依赖于在子类构造函数中执行的初始化的方式继承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设为final:

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子类化。这是来自 Effective Java 的第16项:&#34;赞成合成而非继承&#34;。

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());