gpt4 book ai didi

java - 为什么在绑定(bind)中调用绑定(bind)值的 get 会引发 stackoverflow 错误

转载 作者:行者123 更新时间:2023-12-02 02:49:57 24 4
gpt4 key购买 nike

当获取绑定(bind)标签的值时,此代码会在标签绑定(bind)中引发堆栈溢出错误。我希望标签最初是“测试”,然后在第一次按下“测试按下”然后“测试按下按下”等等。但是,读取该值会引发堆栈溢出错误,因为调用 getText() 方法会触发绑定(bind)。我希望只有按钮按下事件才能触发绑定(bind)。

注意:我已经注释掉了导致错误的代码,并添加了另一个按钮以更好地显示我感到困惑的内容。

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);
}
}

我的目标是建立一个在特定条件下不会更改文本的绑定(bind)。 Callable Lambda 中的逻辑类似于:

if(condition){
return "ok";
}else{
return l.getText(); //if the condition is not met then use the current value.
}

我知道我可以在按下的属性上使用监听器,然后以这种方式设置文本标签,所以我有一个解决方案,但我想知道为什么会发生上述情况。

最佳答案

仅从语义上讲,您的绑定(bind)表达了这样的规则:标签的文本是与 “按下” 连接的标签文本。显然,这表明标签的文本依赖于标签的文本,因此它是递归的。

我不认为这是你想要强加的规则。我想你想要规则为“如果未按下按钮,则标签的文本为 “test”;如果按下按钮,则为 “测试按下”。(现在您的绑定(bind)被告知重新计算按钮的 pressed 属性是否发生变化,但该值实际上并不依赖于该属性。)

从技术上讲,发生的事情大致如下:

public class Label {

private final StringProperty textProperty = new SimpleStringProperty() ;

public String getText() {
return textProperty().get();
}

// ...
}

public class SimpleStringProperty {

private StringBinding binding ;
private boolean bound ;
private String value ;

// ...

public String get() {
if (bound) {
value = binding.get();
}
return value ;
}

public void bind(StringBinding binding) {
bound = true ;
this.binding = binding ;
value = binding.get();
}
}

最后,字符串绑定(bind)的逻辑如下:

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() 方法。

因此,当您创建绑定(bind)时,标签的文本属性的值是根据绑定(bind)的值设置的。绑定(bind)无效(因为尚未计算),因此它是通过您提供的方法计算的。该方法调用label.getText(),它从属性中获取值。由于该属性已绑定(bind),因此它会检查绑定(bind),但该绑定(bind)仍然无效(因为其值的计算尚未完成),因此它会计算其值,从而调用 label.getText()...

所以你可能想要这样的东西:

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("")));

关于java - 为什么在绑定(bind)中调用绑定(bind)值的 get 会引发 stackoverflow 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44010162/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com