TableColumn示例中ReadOnlyObjectWrapper用法困惑及JavaDoc疑问
问题背景
查看TableColumn的JavaDoc时,能看到如下示例代码:
firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() { public ObservableValue<String> call(CellDataFeatures<Person, String> p) { return new ReadOnlyObjectWrapper(p.getValue().getFirstName()); } });
该示例的意图是将String值转换为只读Observable传递给单元格。但查看ReadOnlyObjectWrapper的JavaDoc说明:
此类为定义只读属性提供便捷方式,创建两个同步属性:一个供外部用户使用的只读属性,一个仅内部使用的可读写属性。
这个类继承自SimpleObjectProperty,仅新增了getReadOnlyProperty()方法,但上述TableColumn示例并未调用此方法。再看JFX 21的TableColumn源码:
public final ObservableValue<T> getCellObservableValue(S var1) { Callback var2 = this.getCellValueFactory(); if (var2 == null) { return null; } else { TableView var3 = this.getTableView(); if (var3 == null) { return null; } else { CellDataFeatures var4 = new CellDataFeatures(var3, this, var1); return (ObservableValue)var2.call(var4); } } }
源码中并未检查返回值是否为ReadOnlyObjectWrapper(或等效类型)并调用getReadOnlyProperty(),只是将结果强制转换为ObservableValue后返回。
核心疑问
- 这是JavaDoc的错误吗?还是我遗漏了什么重要内容?
- 既然SimpleObjectProperty也可以像TableColumn源码那样转换为ReadOnlyObjectProperty或ObservableValue,那ReadOnlyObjectWrapper的适用场景是什么?何时应该使用
ReadOnlyObjectWrapper.getReadOnlyProperty()?
解答
1. JavaDoc示例的问题
这个示例不算严格意义上的错误,但写法不够严谨。
因为ReadOnlyObjectWrapper本身是ObservableValue的子类,直接返回它确实能满足TableColumn的需求——TableColumn只需要一个ObservableValue来监听数据变化,从而更新单元格显示。但示例注释明确说要返回“只读Observable”,而直接返回ReadOnlyObjectWrapper的话,外部代码可以通过强转(因为它继承自SimpleObjectProperty)修改其值,这就违背了“只读”的设计意图。
正确的写法应该是返回new ReadOnlyObjectWrapper(p.getValue().getFirstName()).getReadOnlyProperty(),这样外部拿到的是真正的只读属性,无法修改值。JavaDoc示例可能是为了简化代码省略了getReadOnlyProperty(),但这和它宣称的“只读”意图不符,属于示例的不严谨。
2. ReadOnlyObjectWrapper的适用场景
ReadOnlyObjectWrapper的核心价值是帮你快速实现“内部可写、外部只读”的属性封装,具体适用场景包括:
- 当你的类需要内部维护一个可修改的属性,但只允许外部代码监听属性变化,不允许外部修改属性值时。
- 对比SimpleObjectProperty:如果用SimpleObjectProperty实现类似需求,你需要手动创建并维护一个对应的ReadOnlyObjectProperty,还要自己处理两个属性的同步逻辑;而ReadOnlyObjectWrapper已经内置了这两个同步的属性,内部可直接修改自身(继承自SimpleObjectProperty的可写能力),对外通过
getReadOnlyProperty()暴露只读入口,无需手动处理同步。
举个实际使用的例子:
public class Person { // 内部可写的属性容器 private final ReadOnlyObjectWrapper<String> firstName = new ReadOnlyObjectWrapper<>(""); // 内部修改方法 public void updateFirstName(String newName) { firstName.set(newName); } // 对外暴露只读属性,供外部监听变化 public ReadOnlyObjectProperty<String> firstNameProperty() { return firstName.getReadOnlyProperty(); } // 对外提供值的只读访问 public String getFirstName() { return firstName.get(); } }
在这个例子中,外部代码只能通过firstNameProperty()拿到只读的ObservableValue,无法修改属性值;而类内部可以通过firstName.set()自由修改,且修改会自动同步到只读属性上,外部监听者能及时收到变化通知。
如果用SimpleObjectProperty实现同样的效果,需要手动编写更多代码来封装只读属性并处理同步,远不如ReadOnlyObjectWrapper便捷。
内容的提问来源于stack exchange,提问作者DaveB

