5.11 プロパティ
5.11.1 クライアントプロパティ
オブジェクトが提供する入出力可能な情報をプロパティと呼びます。Java の標準クラスライブラリでは set*() メソッドや get*() メソッドまたは is*() メソッドという名前を使ってフィールドと間接的にやり取りできる設計を採用しています。このように、変数のメンバにアクセスするためのメソッドをアクセッサと呼ぶことがありますが、フィールドを参照するためにわざわざアクセッサメソッドを通すのは理由があります。
アクセッサメソッドを作成すれば、サブクラスでメソッドをオーバーライドして、入出力する値を独自のフィルタに通すような拡張を容易にすることができます。また、複数のスレッドからフィールドを不規則に変更されたとき、整合性が重要なデータであればメソッドで同期を取ることも可能になります。さらに、プロパティに設定可能な値の範囲などを定め、それ以外であれば例外をスローして通知するといったこともアクセッサメソッドを通せば可能になるのです。こうした理由から、多くのオブジェクト指向プログラミングの設計者はオブジェクトの情報に対してアクセッサメソッドを仲介させます。
ところが、これとは別に Swing の JComponent クラスはクライアントプロパティと呼ばれる機能を提供しています。クライアントプロパティとはクラスの設計者ではなく、クラスの利用者が自由に定め、設定、及び取得することが可能な動的なプロパティです。クライアントプロパティはクラスではなくインスタンス単位で割り当てられ、プロパティを識別するキーオブジェクトと、プロパティの値で構成されます。
クライアントプロパティを設定するには JComponent クラスの putClientProperty() メソッドを、取得するには getClientProperty() メソッドを使います。
public final void putClientProperty(Object key, Object value)
public final Object getClientProperty(Object key)
key に指定するオブジェクトはプロパティを識別するためのオブジェクトです。これは、オブジェクトがもつ値ではなく、インスタンスそのものがプロパティを指すキーとなります。そのため、同一のオブジェクトをキーとして渡せば、値を渡したり、取得したりできるようになるのです。一般的には、キーにするオブジェクトは文字列がわかりやすいと思われます。
putClientProperty() メソッドで、何らかのキーを用いて渡した値は、getClientProperty() メソッドに同じキーを渡すことで取得することができます。新しいオブジェクトをキーとして渡せば、必要な数だけ新しいプロパティを作成することができます。設定されている値を削除したければ NULL を値に指定してください。
import javax.swing.*; import java.awt.*; public class Test { public static void main(String args[]) { LabelEx label = new LabelEx(); label.putClientProperty(LabelEx.LABEL_TEXT , "Kitty on your lap"); label.setFont(new Font("Serif" , Font.PLAIN , 20)); JFrame win = new JFrame(); win.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); win.setBounds(10 , 10 , 400 , 300); win.getContentPane().add(label); win.show(); } } class LabelEx extends JComponent { public static final String LABEL_TEXT = "LabelEx.Text"; public void paint(Graphics g) { String label = getClientProperty(LABEL_TEXT).toString(); if (label != null) { FontMetrics fm = getFontMetrics(getFont()); g.drawString( label , getWidth() / 2 - fm.stringWidth(label) / 2 , getHeight() / 2 + fm.getDescent() ); } } }
コード1は、クライアントプロパティを用いてコンポーネントが描画するテキストを通信する LabelEx クラスを定義しています。このクラスは、公開している静的な文字列フィールド LABEL_TEXT をキーとしたクライアントプロパティの値を文字列として解釈し、コンポーネントに描画します。main() メソッドでは LabelEx クラスのインスタンスを生成し、これに putClientProperty() メソッドから LABEL_TEXT をキーとして文字列を設定しています。 プログラムを実行すれば、クライアントプロパティの機能を使ってデータが正しく届けられていることを確認することができます。
ただし、クライアントプロパティは、コンポーネントの外からオブジェクトにデータ交換などを目的としてデータを追加するためのもので、クラスの設計でクライアントプロパティを利用するべきではありません。クライアントプロパティは、サブクラスでオーバーライドしたり、リフレクションで直接制御できないなどの制限があるためです。また、キーから値を検索するオーバーヘッドも懸念材料となります。
クライアントプロパティは、動的なプロパティを生成する意味がある場合にのみ利用してください。