何时使用JavaFX属性的setter和getter方法,而不是直接使用属性?

11

我熟悉Java,但刚开始学习JavaFX,特别是学习JavaFX属性。我理解基本的设计模式,就像Oracle的下面例子所示:

package propertydemo;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

class Bill {

    // Define a variable to store the property
    private DoubleProperty amountDue = new SimpleDoubleProperty();

    // Define a getter for the property's value
    public final double getAmountDue(){return amountDue.get();}

    // Define a setter for the property's value
    public final void setAmountDue(double value){amountDue.set(value);}

     // Define a getter for the property itself
    public DoubleProperty amountDueProperty() {return amountDue;}

}

我不理解的是何时/为什么要使用getter和setter方法,而不是直接使用属性?

我想的是你可能希望在getter和/或setter中添加一些自定义代码,以进行数据的预处理或后置操作/验证。但如果创建自定义的getter和/或setter,则会根据您是使用getter/setter还是直接使用属性来获得不同的结果,这对我来说似乎很危险。

如果getter/setter只是调用Property的get和set方法,那么为什么需要它们呢?

如果您有任何见解,将不胜感激。


学习JavaFX电子书中,我们可以看到:为了使类与使用旧的JavaBeans命名约定来识别类属性的旧工具和框架互操作,额外的getter和setter方法将按照JavaBeans命名约定添加。 - Kachna
1
在我看来,不需要额外的getter和setter。 - Kachna
@Kachna 当然这取决于你的使用情况。例如,如果你想在JPA实体中使用JavaFX属性,你需要get和set方法。同样地,如果你正在使用JSON或XML编组等技术,也是如此。 - James_D
1个回答

19
JavaFX属性模式旨在扩展旧的标准JavaBean模式。因此,在您的示例中,根据JavaBean约定,您有一个名为amountdouble类型的(可读写)属性。这是由两个方法确定的。
public double getAmount() ;
public void setAmount(double amount);

JavaBean模式允许通过"绑定属性"进行一些有限的"可观察性",其中bean支持注册PropertyChangeListener。UI工具包通常需要观察属性并响应更改。例如,一个Label具有text属性是有意义的。如果text属性更改,Label需要被通知,以便它知道重新绘制自己。乍一看,使用具有绑定属性的JavaBeans可能是实现这一点的一种方法。但是,在UI工具包中使用此机制会产生性能问题,因为没有办法在不立即计算它的情况下通知值不再有效。这意味着,例如,布局将在每个单独的属性更改时重新计算。
JavaFX团队显然的目标是定义一种模式,该模式符合标准的JavaBean模式,并支持可观察属性,其中可以跟踪无效状态而无需每次更改值时重新计算依赖值("惰性可观察值")。
因此,JavaFX的解决方案是创建支持ChangeListenerInvalidationListener的属性。当值发生变化时,ChangeListener会被通知,当值不再有效时,InvalidationListener会被通知。这意味着,例如,布局机制可以跟踪它当前是否有效,而不会在其无效时强制重新计算。布局仅会在实际屏幕脉冲(即场景渲染时)上重新计算,并且仅在其无效时才会重新计算。(作为一个快速概念验证,请考虑以下内容:)
DoubleProperty width = new SimpleDoubleProperty(3);
DoubleProperty height = new SimpleDoubleProperty(4);
ObservableDoubleValue area = Bindings.createDoubleBinding(() -> {
    double a = width.get() * height.get();
    System.out.println("Computed area: "+a);
    return a ;
}, width, height);
System.out.println("Area is "+area.getValue());
width.set(2);
height.set(3);
System.out.println("Area is "+area.getValue());

请注意,在width为2且height仍为4时,中间值永远不会被计算。

因此,在JavaFX中,值由这些可观察的属性表示,它们支持无效化监听器和更改监听器,这意味着它们基本上是“懒观察”的。通过属性访问器方法(例如您的示例中的amountProperty())公开属性本身就足以支持此功能。

从语义上讲,暴露一个 DoubleProperty 意味着 bean 具有 double 类型的值。为了与旧的 JavaBean 约定保持兼容性,该 bean 应通过公开相应的 getset 方法来广告这一事实。因此,JavaFX 属性模式需要 "属性访问器"(amountProperty())以及标准的 JavaBean 方法(getAmount()setAmount(...))。这意味着遵循 JavaFX 模式的 beans 可以在任何使用标准 JavaBean 模式的地方使用,例如在 JPA 中。
请注意,为了使模式正常工作,应始终满足 amountProperty().get() == getAmount()amountProperty().set(x) 具有与 setAmount(x) 相同的效果。通过将 getset 方法设为 final(就像您的示例中一样),可以保证这一点,即使 bean 类被子类化也是如此。
如果您自己调用方法来检索或更改属性的值,则无论您调用哪个方法,它们都保证具有相同的效果。由于JavaFX Property模式是JavaBean模式的扩展,因此可能稍微倾向于调用getset方法:从某种意义上说,访问值只需要JavaBean功能,而不是完整的JavaFX属性功能,因此仅依赖该功能可能在语义上有些合理。但实际上,您使用哪个方法并没有任何区别。

1
感谢您的详细解释。如果setter实际上操作数据,例如您有一个StringProperty firstName:firstName.set(name.trim())在这种情况下,调用setter方法与在属性上调用set()不同,或者这是JavaFX属性中的不允许行为。 - vewert
抱歉,我想添加一个更详细的示例,但似乎您无法在评论中添加代码块。 - vewert
1
通过那个例子,你会违反多个约定。如前所述,在JavaFX属性模式中,您应确保setFirstName(x)等同于firstNameProperty().set(x)。但在常规Java Bean模式中,您应该确保setFirstName(x); getFirstName()计算为x,假设两个方法调用之间对象没有发生其他操作。如果您真的想做类似的事情,请继承StringPropertyBase并重写setString(...)setValue(String) - James_D
@James_D,你是不是指覆盖set(String)和String get()方法?特别是,StringPropertyBase类似乎没有setString方法。 - user118967

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接