かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Beans Bindingしてみた

NetBeans6.0M10についてきてるライブラリにBeans Bindingっていうのがある。
気になったので触ってみた。

きしださんのBlogから見れるデモの裏でもNetBeansがガシガシBeans Binding使いまくってる。

NetBeans6.0M10のGUIのデザイナが吐き出すバインドのコードを見た限りだと基本的な流れは下のようになるみたいだ。

  • javax.beans.binding.BindingContextのインスタンスを作る
  • javax.beans.binding.BindingContext#addBindingでバインドする
  • javax.beans.binding.BindingContext#bindメソッドを呼ぶ

これでバインドされるっぽい。

addBindingメソッド

addBindingメソッドには色々なオーバーライドがあるけど、一番お手軽に使えるのは下の形のだと思う。

Binding addBinding(Object beans1, String el, Object beans2, String propertyName);

第一引数:なんかのJavaBeans
第二引数:なんかEL式
第三引数:なんかのJavaBeans
第四引数:プロパティ名

第一引数と第二引数で指定されたプロパティと、第三引数と第四引数で指定されたプロパティをバインドする。

Beans Binding対応のクラスを作る

とりあえず、クラスを1つ作る。
説明もいらないくらいシンプルなやつです。

package okazuki;

public class Person {
    private String name;
    public Person() {
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

最初は、これでバインディングを試してたけど上手くバインドされませんでした。
Beans Bindingは魔法のような仕掛けでプロパティをバインドしてくれているわけじゃなくて、PropertyChangeListenerを使ってバインドしてるみたいです。
ってことでaddPropertyChangeListenerとremovePropertyChangeListenerを追加して最終的に下のようなクラスを作りました。

package okazuki;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.LinkedList;
import java.util.List;

public class Person {
    private String name;
    private List<PropertyChangeListener> propertyChangeListeners = new LinkedList<PropertyChangeListener>();
    public Person() {
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        String oldName = this.name;
        this.name = name;
        firePropertyChange("name", oldName, name);
    }
    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeListeners.add(l);
    }
    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeListeners.remove(l);
    }
    protected void firePropertyChange(String name, Object oldValue, Object newValue) {
        PropertyChangeEvent evt = new PropertyChangeEvent(this, name, oldValue, newValue);
        for (PropertyChangeListener l : propertyChangeListeners) {
            l.propertyChange(evt);
        }
    }
}

バインドしてみる

さっき作ったクラスを実際にバインドしてみようと思う。
とりあえず初バインドなので、Personクラスのnameプロパティ同士のバインドくらいに挑戦する。

package okazuki;

import javax.beans.binding.BindingContext;

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();
        
        BindingContext ctx = new BindingContext();
        ctx.addBinding(p1, "${name}", p2, "name");
        ctx.bind();
        
        System.out.println("p1.name change");
        p1.setName("太郎");
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());
        
        System.out.println("p2.name change");
        p2.setName("二郎");
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());
    }
}

実行すると下のような出力になる。

p1.name change
p1.name = 太郎
p2.name = 太郎
p2.name change
p1.name = 二郎
p2.name = 二郎

p1のnameとp2のnameをバインドすると、p1のnameを変えるだけでp2のnameまで書き換わってるのがわかると思う。
逆もしかり。
どうでもいいけど、第二引数ではEL式を要求しといて第三引数ではプロパティ名を要求するってのは何でだろう?
両方ともEL式でいいような気がする。


ついでに、いつPropertyChangeListenerが追加されてるのか調べてみた。
どうも、BindingContext#bindが呼ばれたタイミングっぽい。