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

Sed*_*ick 3 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Jam*_*s_D 5

这是 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 ;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果FlashCard构造函数被调用this.getChildren(),那么调用子类构造函数将抛出 a ,因为超类构造函数将尝试在初始化之前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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

另一种方法(我倾向于更喜欢)是Pane首先不进行子类化。这是《Effective 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 ;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

FlashCard flashCard = new FlashCard(6, 9, 42);
someContainer.getChildren().add(flashCard);
Run Code Online (Sandbox Code Playgroud)

你做

FlashCard flashCard = new FlashCard(6, 9, 42);
someContainer.getChildren().add(flashCard.asPane());
Run Code Online (Sandbox Code Playgroud)