いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

プロパティの変更を通知する仕組みを作成する

Swingなどを利用していると、プロパティの変更を通知する仕組みが備わっています。
これは、各コンポーネントがJavaBeansとして動作するようになっているためです。
プロパティが変更された際にその通知を受け取ることができると何かと便利です。
この仕組みは別にSwingだけじゃなくて自分の作ったクラスでも、それをサポートすることができます。

ちなみに、値の変更が発生した際に通知が行われるプロパティのことをバウンドプロパティ(bound property)といいます。

作成方法は、以下の手順となります。

  1. クラス内にjava.beans.PropertyChangeSupportオブジェクトの参照を保持.
  2. addPropertyChangeListenerメソッドとremovePropertyChangeListenerメソッドを実装(任意)
  3. 上記のメソッドの中身をPropertyChangeSupportへの委譲処理とする.
  4. バウンドプロパティの値が変更される箇所で、PropertyChangeSupport.firePropertyChangeメソッドをコール.(通常、セッターメソッド等)

以下、サンプルです。

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

/**
 * プロパティの変更を通知する仕組みのテストクラスです.<br/>
 *
 * @author gsf_zero1
 *
 */
public class TestBean1 {
  
    /** プロパティの変更通知をサポートするオブジェクト */
    private PropertyChangeSupport changes;
    
    /** NAME */
    private String  name;
    
    /** AGE */
    private Integer age;
    
    /** Creates a new instance of TestBean1 */
    public TestBean1() {
        changes = new PropertyChangeSupport(this);
    }
    
    /**
     * リスナの追加を行います.<br/>
     * @param l 追加するリスナ
     */
    public void addPropertyChangeListener(PropertyChangeListener l){
        changes.addPropertyChangeListener(l);
    }
    
    /**
     * リスナの削除を行います.<br/>
     * @param l 削除するリスナ
     */
    public void removePropertyChangeListener(PropertyChangeListener l){
        changes.removePropertyChangeListener(l);
    }
    
    /**
     * NAMEを取得します.<br/>
     * @return NAME
     */
    public String getName() {
        return name;
    }
    
    /**
     * NAMEを設定します.<br/>
     * @param name NAME
     */
    public void setName(String name) {
        
        String oldValue = getName();
        String newValue = name;
        
        this.name = name;
        
        this.changes.firePropertyChange("name", oldValue, newValue);
    }
    
    /**
     * AGEを取得します.<br/>
     * @return AGE
     */
    public Integer getAge() {
        return age;
    }
    
    /**
     * AGEを設定します.<br/>
     * @param age AGE
     */
    public void setAge(Integer age) {
        Integer oldValue = getAge();
        Integer newValue = age;
        
        this.age = age;
        
        this.changes.firePropertyChange("age", oldValue, newValue);
    }
    
    /**
     * アプリケーションエントリポイントです.<br/>
     * @param args 起動時引数
     */
    public static void main(String[] args) {
        TestBean1                       t        = new TestBean1();
        TestBean1PropertyChangeListener listener = new TestBean1PropertyChangeListener();
        
        t.addPropertyChangeListener(listener);
        
        t.setName("gsf_zero1");
        t.setAge(99);
        
        t.setName("gsf_zero2");
        t.setAge(18);
        
        t.removePropertyChangeListener(listener);
        
        t.setName("gsf_zero3");
        t.setAge(20);
    }
}

/**
 * TestBean1用のテストリスナです.<br/>
 * @author gsf_zero1
 */
class TestBean1PropertyChangeListener implements PropertyChangeListener{
    
    /**
     * プロパティの変更イベント処理を行います.<br/>
     * @param evt イベントオブジェクト
     */
    public void propertyChange(PropertyChangeEvent evt) {
        
        System.out.printf(
                "source:%s, propertyName:%s, oldValue:%s, newValue:%s\n",
                evt.getSource(),
                evt.getPropertyName(),
                evt.getOldValue(),
                evt.getNewValue());
        
    }
    
}

実行すると以下のようになります。

source:TestBean1@1a758cb, propertyName:name, oldValue:null, newValue:gsf_zero1
source:TestBean1@1a758cb, propertyName:age, oldValue:null, newValue:99
source:TestBean1@1a758cb, propertyName:name, oldValue:gsf_zero1, newValue:gsf_zero2
source:TestBean1@1a758cb, propertyName:age, oldValue:99, newValue:18

リスナを削除した後は、値を変更しても通知が行われていないことも確認できます。
Swingでは、頻繁にPropertyChangeListenerを利用したりしますので知ってると得です。
(時間がかかる処理を行っている間のみ、モーダルのダイアログを表示したりする等)