CUBは子供の白熊

Java SE 8 実践プログラミングの練習問題を解く

第4章 JavaFX による GUI プログラミング : 問題 5 : ObservableValue とラムダ式

問題

次の二つのメソッドを実装せよ

public static <T,R> ObservableValue<R> observe(Function<T,R> f, ObservableValue<T> t)
public static <T,U,R> ObservableValue<R> observe(BiFunction<T,U,R> f,
                                                 ObservableValue<T> t, ObservableValue<U> u)

このメソッドは、与えられたObservableValuegetValueメソッドFunctionまたはBiFunctionで変換する

そして、元のObservableValueで Invalid イベントまたは Change イベントが起こったときは、observeメソッドが生成したObservableValueでもイベントが起こるようにせよ

ObservableValue について

もはや驚いたりはしないが、この本では本文に何の説明もないのに練習問題でいきなり登場することがある

ObservableValue<T>は、本文で取り上げられたBinding<T>Property<T>の共通の祖先である

■ ObservableValue と Binding, Property の関係
f:id:ClosedUnBounded:20150723205241p:plain

つまりObservableValueは Invalid イベントと Change イベントを受けられる Value である

解答

ゲージ(Rectangle)の長さとボタンが連動するサンプルソースで検証する

■ オリジナル

import static javafx.beans.binding.Bindings.*;

public void start(Stage stage) throws Exception {
    Button smaller = new Button("Smaller");
    Button larger  = new Button("Larger");
    Rectangle gauge   = new Rectangle(0, 5,  50, 15);
    Rectangle outline = new Rectangle(0, 5, 100, 15);
    outline.setFill(null);
    outline.setStroke(Color.BLACK);
    Pane pane = new Pane();
    pane.getChildren().addAll(gauge, outline);
    // ボタンクリック時のアクション
    smaller.setOnAction(event -> gauge.setWidth(gauge.getWidth() - 10));
    larger .setOnAction(event -> gauge.setWidth(gauge.getWidth() + 10));
    // ゲージの長さとボタンの有効/無効
    smaller.disableProperty().bind(lessThanOrEqual(   gauge.widthProperty(),   0));
    larger .disableProperty().bind(greaterThanOrEqual(gauge.widthProperty(), 100));

    HBox box = new HBox(10);
    box.getChildren().addAll(smaller, pane, larger);
    Scene scene = new Scene(box);
    stage.setScene(scene);
    stage.show();
}

これをobserveメソッドを使って書き直す

observeメソッドを使用

// ゲージの長さとボタンの有効/無効
smaller.disableProperty().bind(
    observe(
        t -> t.doubleValue() <= 0.0,
        gauge.widthProperty()
    )
);
larger.disableProperty().bind(
    observe(
        t -> t.doubleValue() >= 100.0,
        gauge.widthProperty()
    )
);

では、observeメソッドを実装してみよう

observeメソッドの実装

import static javafx.beans.binding.Bindings.*;

public static <T,R> ObservableValue<R> observe(Function<T,R> f, ObservableValue<T> t) {
    return createObjectBinding(
        () -> f.apply(t.getValue()),
        t
    );
}

public static <T,U,R> ObservableValue<R> observe(BiFunction<T,U,R> f,
                                                 ObservableValue<T> t, ObservableValue<U> u) {
    return createObjectBinding(
        () -> f.apply(t.getValue(), u.getValue()),
        t, u
    );
}