簡體   English   中英

為什么在綁定中調用get的綁定值會引發stackoverflow錯誤

[英]Why does calling get of a bound value in the binding throw a stackoverflow error

獲取綁定的標簽的值時,此代碼在標簽綁定中引發stackoverflowerror。 我希望標簽最初是“測試”,然后在第一次按下“測試按下”,然后是“按下測試”,依此類推。 但是,讀取值會引發stackoverflowerror,因為調用getText()方法會觸發綁定。 我希望只有按鈕按下事件才能觸發綁定。

注意:我已注釋掉導致錯誤的代碼,並添加了另一個按鈕以更好地顯示我的困惑。

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

我的目標是在特定條件下具有不會更改文本的綁定。 Callable Lambda中的邏輯類似於:

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

我知道我可以在被按下的屬性上使用偵聽器,然后以這種方式設置文本標簽,因此我有一個解決方案,但我想知道為什么會發生上述情況。

僅在語義上,綁定表示規則,即標簽的文本是與" pressed"串聯的標簽的文本。 顯然,這是說標簽的文本取決於標簽的文本,因此它是遞歸的。

無論如何,我認為這不是您要強加的規則。 我認為您希望規則為:如果未按下按鈕,則標簽文本為"test"如果未按下按鈕,則規則為"test pressed" 。(現在,如果按鈕的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();
    }
}

最后,字符串綁定具有以下幾方面的邏輯:

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

因此,當您創建綁定時,將從綁定的值中設置標簽的text屬性的值。 綁定無效(因為尚未計算),因此通過您提供的方法進行計算。 該方法調用label.getText() ,該方法從屬性獲取值。 因為該屬性已綁定,所以它將檢查仍然無效的綁定(因為尚未完成其值的計算),因此它將計算其值,從而調用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("")));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM