Java 为什么在绑定中调用绑定值的get会引发stackoverflow错误
当获取要绑定的标签的值时,此代码在标签绑定中抛出StackOverflower错误。我希望标签最初是“test”,然后第一次按“test pressed”,然后按“test pressed”,依此类推。但是,读取该值会抛出StackOverflowerError,因为调用getText()方法会触发绑定。我期望只有按钮按下事件触发绑定 注意:我已经注释掉了导致错误的代码,并添加了另一个按钮,以更好地显示我的困惑Java 为什么在绑定中调用绑定值的get会引发stackoverflow错误,java,javafx,binding,stack-overflow,bind,Java,Javafx,Binding,Stack Overflow,Bind,当获取要绑定的标签的值时,此代码在标签绑定中抛出StackOverflower错误。我希望标签最初是“test”,然后第一次按“test pressed”,然后按“test pressed”,依此类推。但是,读取该值会抛出StackOverflowerError,因为调用getText()方法会触发绑定。我期望只有按钮按下事件触发绑定 注意:我已经注释掉了导致错误的代码,并添加了另一个按钮,以更好地显示我的困惑 import javafx.application.Application; imp
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application{
@Override
public void start(Stage primaryStage) {
Label l = new Label("test");
Button b = new Button("press me");
l.textProperty().bind(Bindings.createStringBinding(() ->{
System.out.println("changing label text");
return "ok";
//return l.getText() + " pressed"; //Causes a stackoverflow error
},b.pressedProperty()));
Button b2 = new Button("press me 2");
b2.pressedProperty().addListener((o) -> {
l.getText(); //Why does this not triggger the binding?
});
VBox root = new VBox();
root.getChildren().addAll(l,b,b2);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Binding test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args){
launch(args);
}
}
我在这里的目标是要有一个在一定条件下不会改变案文的约束力。可调用Lambda中的逻辑类似于:
if(condition){
return "ok";
}else{
return l.getText(); //if the condition is not met then use the current value.
}
label.textProperty().bind(Bindings.createStringBinding(() -> {
if (b.isPressed()) {
return "test pressed";
} else {
return "test";
}
}, b.pressedProperty());
我知道我可以在pressed属性上使用侦听器,然后以这种方式设置文本标签,所以我有一个解决方案,但我想知道为什么会发生上述情况 就语义而言,您的绑定表达了这样一条规则:标签的文本是与
“pressed”
连接的标签文本。显然,这意味着标签的文本依赖于标签的文本,因此它是递归的
我认为这不是你想强加的规则。我想你想要这个
规则为“未按下按钮时标签文本为”测试“
,按下按钮时标签文本为”测试“
。(现在,如果按钮的按下属性发生变化,绑定被告知重新计算,但值实际上并不取决于该属性。)
从技术上讲,正在发生的事情大致如下:
public class Label {
private final StringProperty textProperty = new SimpleStringProperty() ;
public String getText() {
return textProperty().get();
}
// ...
}
public abstract class StringBinding {
private boolean valid = false;
private String value ;
protected void bind(ObservableStringValue dependency) {
dependency.addListener(o -> invalidate());
}
private void invalidate() {
valid = false ;
// notify invalidation listeners...
}
public String get() {
if (!valid) {
value = computeValue();
valid = true ;
}
return value ;
}
public abstract String computeValue();
}
及
最后,字符串绑定具有以下逻辑:
public class Label {
private final StringProperty textProperty = new SimpleStringProperty() ;
public String getText() {
return textProperty().get();
}
// ...
}
public abstract class StringBinding {
private boolean valid = false;
private String value ;
protected void bind(ObservableStringValue dependency) {
dependency.addListener(o -> invalidate());
}
private void invalidate() {
valid = false ;
// notify invalidation listeners...
}
public String get() {
if (!valid) {
value = computeValue();
valid = true ;
}
return value ;
}
public abstract String computeValue();
}
在您的示例中,computeValue()
的实现调用标签的getText()
方法
因此,当您创建绑定时,标签文本属性的值是根据绑定的值设置的。绑定无效(因为尚未计算),因此它是通过您提供的方法计算的。该方法调用label.getText()
,它从属性获取值。由于属性已绑定,它会检查绑定,该绑定仍然无效(因为其值的计算尚未完成),因此它会计算其值,从而调用label.getText()
因此,您可能需要以下内容:
if(condition){
return "ok";
}else{
return l.getText(); //if the condition is not met then use the current value.
}
label.textProperty().bind(Bindings.createStringBinding(() -> {
if (b.isPressed()) {
return "test pressed";
} else {
return "test";
}
}, b.pressedProperty());
如果希望基础字符串能够更改,则需要为其创建新属性:
StringProperty text = new SimpleStringProperty("test");
label.textProperty().bind(Bindings.createStringBinding(() -> {
if (b.isPressed)() {
return text.get() + " pressed" ;
} else {
return text.get();
}
}, text, b.pressedProperty());
或者,相当于
label.textProperty().bind(text.concat(
Bindings.when(b.pressedProperty())
.then(" pressed")
.otherwise("")));
就语义而言,您的绑定表达了这样一条规则:标签的文本是与“pressed”
连接的标签文本。显然,这意味着标签的文本取决于标签的文本,因此它是递归的
我不认为这是你想要强加的规则。我认为你想要
如果未按下按钮,则规则为“标签文本为”测试“
,如果按下按钮,则规则为“标签文本为”测试“
。(现在,如果按钮的pressed
属性发生更改,绑定将被告知重新计算,但该值实际上并不依赖于该属性。)
从技术上讲,正在发生的事情大致如下:
public class Label {
private final StringProperty textProperty = new SimpleStringProperty() ;
public String getText() {
return textProperty().get();
}
// ...
}
public abstract class StringBinding {
private boolean valid = false;
private String value ;
protected void bind(ObservableStringValue dependency) {
dependency.addListener(o -> invalidate());
}
private void invalidate() {
valid = false ;
// notify invalidation listeners...
}
public String get() {
if (!valid) {
value = computeValue();
valid = true ;
}
return value ;
}
public abstract String computeValue();
}
及
最后,字符串绑定具有以下逻辑:
public class Label {
private final StringProperty textProperty = new SimpleStringProperty() ;
public String getText() {
return textProperty().get();
}
// ...
}
public abstract class StringBinding {
private boolean valid = false;
private String value ;
protected void bind(ObservableStringValue dependency) {
dependency.addListener(o -> invalidate());
}
private void invalidate() {
valid = false ;
// notify invalidation listeners...
}
public String get() {
if (!valid) {
value = computeValue();
valid = true ;
}
return value ;
}
public abstract String computeValue();
}
在您的示例中,computeValue()
的实现调用标签的getText()
方法
因此,在创建绑定时,标签的文本属性的值是根据绑定的值设置的。绑定无效(因为尚未计算该绑定),因此它是通过您提供的方法计算的。该方法调用label.getText()
,它从属性中获取值。由于属性已绑定,因此它会检查绑定,该绑定仍然无效(因为其值的计算尚未完成),因此它会计算其值,从而调用label.getText()
因此,您可能需要以下内容:
if(condition){
return "ok";
}else{
return l.getText(); //if the condition is not met then use the current value.
}
label.textProperty().bind(Bindings.createStringBinding(() -> {
if (b.isPressed()) {
return "test pressed";
} else {
return "test";
}
}, b.pressedProperty());
如果希望基础字符串能够更改,则需要为其创建新属性:
StringProperty text = new SimpleStringProperty("test");
label.textProperty().bind(Bindings.createStringBinding(() -> {
if (b.isPressed)() {
return text.get() + " pressed" ;
} else {
return text.get();
}
}, text, b.pressedProperty());
或者,相当于
label.textProperty().bind(text.concat(
Bindings.when(b.pressedProperty())
.then(" pressed")
.otherwise("")));
您的绑定表达了这样一条规则,即标签文本是与“pressed”
连接的标签文本。。。它是无限的recursive@James_D我知道逻辑是递归的,但是触发递归的条件应该是Bindings.createStringBinding()绑定到的可观察值(b.pressed),而不是标签的值。但是绑定的值是什么?基本上,当计算绑定的值时,您必须计算标签的label.getText()
,它查看标签的textProperty()
的值。该值在无效后尚未设置(因为它仍在设置过程中),因此要获得该值,必须计算绑定。绑定的值应为l.getText()+“pressed”。我希望该值绑定到pressed属性,而不是它本身。是的,但是l.getText()
返回什么?您的绑定表示标签文本是标签文本与“pressed”
连接的规则。。。它是无限的recursive@James_D我知道逻辑是递归的,但是触发递归的条件应该是Bindings.createStringBinding()绑定到的可观察值(b.pressed),而不是标签的值。但是绑定的值是什么?基本上,当计算绑定的值时,您必须计算标签的label.getText()
,它查看标签的textProperty()
的值。该值在无效后尚未设置(因为它仍在设置过程中),因此要获得该值,必须计算绑定。绑定的值应为l.getText()+“pressed”。我希望值绑定到pressed属性,而不是它本身。是的,但是l.getText()
返回什么?我想要的规则是