我有一个奇怪的情况,在System.gc()调用之后,ComboBox和IntegerProperty之间的双向绑定始终断开。不知何故,如果我使用ObjectProperty,一切都会按预期进行。
我附上了下面的所有代码。当应用程序开始运行时,标签会正确显示ComboBox1和ComboBox2的相应选定值。但是,一旦单击GC按钮,ComboBox1和fubar.mID1属性之间的双向绑定就会中断。
我知道双向绑定是一个弱绑定,一旦对象超出范围,gc就会清除它。但是这里没有一个对象超出范围。为什么?
这是MainController.java:
package sample;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.util.StringConverter;
public class MainController {
private final Fubar fubar = new Fubar(1, 2);
@FXML
ComboBox<Integer> mComboBox1;
@FXML
ComboBox<Integer> mComboBox2;
@FXML
Label mLabel;
@FXML
public void initialize() {
mComboBox1.setConverter(new StringConverter<Integer>() {
@Override
public String toString(Integer integer) {
if (integer == null)
return "";
switch (integer) {
case 1:
return "One";
case 2:
return "Two";
default:
return "";
}
}
@Override
public Integer fromString(String s) {
if (s == null)
return 0;
switch (s) {
case "One":
return 1;
case "Two":
return 2;
default:
return 0;
}
}
});
for (int i = 0; i < 3; i++)
mComboBox1.getItems().add(i);
mComboBox1.valueProperty().bindBidirectional(fubar.getID1Property().asObject());
mComboBox2.setConverter(mComboBox1.getConverter());
for (int i = 0; i < 3; i++)
mComboBox2.getItems().add(i);
mComboBox2.valueProperty().bindBidirectional(fubar.getID2Property());
mLabel.textProperty().bind(Bindings.createStringBinding(() ->
fubar.getID1Property().get() + "/" + fubar.getID2Property().get(),
fubar.getID1Property(), fubar.getID2Property()));
}
@FXML
private void handleGC() {
System.gc();
}
}
这是Fubar.java:
package sample;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
public class Fubar {
private final IntegerProperty mID1 = new SimpleIntegerProperty(0);
private final ObjectProperty<Integer> mID2 = new SimpleObjectProperty<>(0);
Fubar(Integer id1, Integer id2) {
mID1.set(id1);
mID2.set(id2);
}
IntegerProperty getID1Property() { return mID1; }
ObjectProperty<Integer> getID2Property() { return mID2; }
}
这是Main.java:
package sample;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
public class Fubar {
private final IntegerProperty mID1 = new SimpleIntegerProperty(0);
private final ObjectProperty<Integer> mID2 = new SimpleObjectProperty<>(0);
Fubar(Integer id1, Integer id2) {
mID1.set(id1);
mID2.set(id2);
}
IntegerProperty getID1Property() { return mID1; }
ObjectProperty<Integer> getID2Property() { return mID2; }
}
这是Main.java:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
primaryStage.setTitle("Main");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
和Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.MainController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<ComboBox fx:id="mComboBox1" prefWidth="150.0" />
<ComboBox fx:id="mComboBox2" prefWidth="150.0" GridPane.rowIndex="1" />
<Button mnemonicParsing="false" onAction="#handleGC" text="GC" GridPane.rowIndex="2" />
<Label fx:id="mLabel" text="Main" GridPane.columnIndex="2" />
</children>
</GridPane>
答案 0 :(得分:0)
啊哈,我知道了。问题出在fubar.getID1Property().asObject()
。我相信Java将此asObject()
视为一个临时对象。临时对象会立即超出范围,而System.gc()
调用只需清除临时对象并破坏绑定。
要解决此问题,需要保留对此asObject()
的引用。在我自己的示例问题中,执行此操作的一种方法是在控制器中声明字段ObjectProperty<Integer> middleMan
并将其初始化为
ObjectProperty<Integer> middleMan = fubar.getID1Property().asObject();
,然后将mCombobox1.valueProperty()
绑定到此middleMan
。