JavaFX の Low-Level Binding API の使い方整理
いやあ、ハマりにハマりまくったが何とか解決できたので整理する。
前に JavaFX の Binding ってあまり使う事ないかも とか言ったけど撤回します。その後調べてると Low-Level Binding とも組み合わせたら割とやりた い放題、と言うか異なる型同士の Property を Bind する方法はこれしかないので色々と弄ってみ てた。
一応最後にちょろっと Clojure の事もあるのでタグ付けしとく。
JavaFX の Low-Level Binding API の挙動とか作法とか
これは自分なりの理解なのだが、javafx.beans.binding.XXXBinding って何をしてるかと言うと、
値の変更監視設定を XXXBinding#bind() の引数へ渡す事で行う
- この時引数に渡す型は Observable でないといけない
- 具体的に Observable なものは Control にある Property だったり、SimpleXXXProperty から生成するものとかがある。
- XXXBinding では Bind する先の型と同じものにするか、それが無理なら ObjectBinding にする。
Bind 先の計算内容を computeValue() に定義する
- Bind 先とは言うもののこのクラス定義からは何へバインドするかは分からない、分かりようがないのでそこは自分で合わせる事。
- bind で監視対象に入った値の変更が発生するとこいつが走る。
- 必ずしも監視対象の Property を使う必要もなく、単に StringBinding にして computeValue() で常に "変わったよ~" なんて返しても良いw
- までの定義がされた XXXBinding オブジェクトを new したものは Observable な為、 someProperty.bind() の引数として渡す事が可能。
公式チュートリアルだと 3 の直接的な説明やサンプルがないんだよな。2. で終わっちゃってる。
3 ができる事を示すサンプルはこちらのブログ記事が良い。 英語だけど Java サンプルコード付きなのでコードだけ見ても何となく分かると思う。項目としては後半の "Custom implemented Bindings" の所。
ハマった点
Java のインスタンスイニシャライザブロック
まず、サンプルでは new 後に匿名クラス定義が書かれてるのは何となく分かったのだが、その後の bind を書いている部分を {} で囲んでいる意味が分からなかった。
これは Java ではインスタンスイニシャライザブロックと呼ばれ、コンストラクタよりも先に実行さ れるものらしい。また、匿名クラス定義時にはコンストラクタを書く事ができないらしく、 それでもインスタンス生成時に何かしらの初期化を行いたい時は唯一の方法っぽい。
このおかげでいちいち別クラスや inner クラスを定義する手間は省けるみたい。
bind は protected final
API doc見ると分かるが bind は protected final なメソッドの為オーバーライド不可。
Java の修飾子によるアクセス制御も良く分かってなかったがこちらがシンプルで分かり易いか。
Javaクラス定義メモ(Hishidama's Java Class define Memo)
Clojure の proxy は protected なメソッドは見えない
clojure で匿名クラス生成となると proxy が思いつくが、これには制限があり protected なメソッ ドを呼び出せない。最初この事を知らずに結構ハマってた。
Clojure の Google Groupなどでもちょくちょくされる質問っぽい。これで doc をしっかり読み直してみると、ちゃんと書いてある。
とは言え、bind が呼び出せないと JavaFX Low-Level Binding 的には困る。この制限を Clojure で回避するには Java リフレクション API の setAccessible メソッド を使うか、Clojure ならば gen-class の :exposes-method で晒しだすメソッドを指定するなどがある。
今回は単に XXXBinding#bind() の実装を呼び出したいだけだったので gen-class で表に出てもらう事にした。つか protected final だったら変更できないんでしょ。
今回ハマった結果何とか出来たコード
できれば cljfx を更新したいのだが、Github の方の使い方をすっかり忘れてるので Gist にスニペットだけ載せます。多分また API インターフェースも変えると思うし。
しかしこれマクロでないとダメなんかな。