torutkのブログ

ソフトウェア・エンジニアのブログ

Beansでプロパティの変更通知失敗コーディング

原因が分かってしまえば他愛もないコーディングミスなのですが、数時間はまってしまいました。
Beansのバウンドプロパティを実装し、プロパティ"level"が更新されたら通知するものです。

class MyBean extends AbstractBean implements Runnable {
    :
    public void setLevel(int aLevel) {
        int oldLevel = level;
        level = aLevel;
        firePropertyChange("level", oldLevel, level);
    }

    public void run() {
        setLevel(++level);  // levelをインクリメントし通知させるつもり
    }

    private int level;
}

タイマーで定期的にrun()を呼ぶことでlevelが増えていくのですが、一向に通知が発生せず数時間はまりました。(おかげで、BeansBindingのコードにログを埋めまくり・・・。)

最初++levelでインクリメントするコードだったのを、バウンドプロパティで通知するためにsetLevelメソッドを呼ぶようにしたのですが、それが落とし穴でした。

教訓

getter・setterメソッド中のコードを除き、フィールドを直接アクセスしてはいけない。

コンストラクタ中でフィールドを直接アクセスするのは?

厳格に教訓に従うならば、コンストラクタ中でもsetterメソッドを使用します。その場合、コンストラクタ中から非finalなメソッドを呼び出すのはよろしくないので、setterメソッドをfinalにします。

バウンドプロパティのGUIへの通知

Beansクラスからプロパティの更新をGUI側に通知する場合、AWT EDT(Event Dispatch Thread)でない場合には注意が必要です。

以下のクラスを使ってバウンドプロパティを実装すれば、非EDTから通知しようとしたときに、EDTに載せ替えて通知してくれます。

  • javax.swing.event.SwingPropertyChangeSupport(JDK標準)
  • org.jdesktop.application.AbstractBean (JSR-296)

今まで、java.beans.PropertyChangeSupportを使っていて、SwingUtilities.invokeLaterメソッドでスレッドの載せ替えをしていましたが、手間が少し減ります。

BeansBindingでDate型のプロパティとString型のプロパティを結びつける

NetBeans 6で搭載されたBeansBinding(JSR-295)を使って、Date型のプロパティを画面上のJLabel部品等で表示するString型にバインドする方法を調べてみました。

JLabelを選択し右クリック[バインド]→[text]で、textプロパティをバインドでXBeansのDate型プロパティを指定します。
このまま実行すると、Date型インスタンスをセットしようとしてClassCastExceptionが発生します。

そこで、コンバータをセットしてDate型とString型を変換するようにします。まず、[詳細]タブで「型変換」のコンバータ欄の[...]をクリックして[converterプロパティを設定]欄を[カスタムコード]にします。
続いて、Converterの匿名クラスのコードを記述します。