JComboBox
今日、仕事仲間の人がはまっていた、私も気づいていなかったJComboBoxの罠。
JComboBoxに追加する要素として以下のようなコードで追加すると、二つ目の"A"を選択しても、コンボボックスのドロップダウンををもう一度開くと、一つ目が選択されてしまっているという・・・
JComboBox comboBox = new JComboBox(); comboBox.addItem( "A" ); comboBox.addItem( "B" ); comboBox.addItem( "A" );
これは、コンボボックスが現在選択されている項目が、選択肢の何れかかを判断するのに equals メソッドを使用しているからと思われる。
ちなみに当たり前ちゃ当たり前ですが、 String クラスの equals メソッドは、そのオブジェクトが表している文字列が等しいかを比較するため、上記のような場合に、二つ目の"A"を選択しても一つ目とも等しいので、コンボボックス的には一つ目の"A"が選択されてしまうというわけです。
なので、下記のような自前の選択肢用クラスで equals メソッドを自分好みにオーバーライドしてやれば解決するというわけ。
#toString もオーバーライドするのを忘れないように。*1
public class Item { private String label; private int key; public Item( int key, String label ) { this.label = label; this.key = key; } public String toString() { return label; } public boolean equals( Object other ) { if( this==other ) return true; if( other==null ) return false; if( !(other instanceof Item) ) return false; // この書き方はあまり好まれないかな・・・ return this.key==((Item)other).key; } }
Cとかなら、コンパイラが同じ内容の文字列リテラルはひとつにまとめたりするんで、上記のようなコードの場合は同じ二つの"A"は同じポインタ値になってしまう可能性もあるわけだが、Javaの場合は文字リテラルはまとめられたりするんだろうか・・・?
どうせ不変オブジェクト(でしかも、String#equalsは内容の比較)だからまとめられたとしても、さして問題があるとは思えないし。実際のところどうなのか、コンパイラとかに詳しい人がコメント書いてくれないかなぁと思ったり。案外言語仕様で決まってたりするのかな?
*1:コンボボックスにデフォルトで設定されているレンダラは、各選択肢オブジェクトのtoString結果を表示するようになっている