static final な List や Map をサクッと宣言しつつ、add() や put() も許さない
Java の final 修飾子は代入を禁止するだけで、インスタンスの内容を変更できなくするわけではない。そのため、リストの内容を追加・変更させないつもりで final
と打つだけでは List#add()
ができてしまうのだ。
// 要素を変更されたくない List を static final で宣言。インスタンスイニシャライザを使って宣言時に要素を入れておく。
private static final List<String> HOGE_LIST = new ArrayList<String>() {{ add("FUGA"); add("PIYO"); }};
public static void main(String[] args) {
// final を指定していても追加はできてしまう
HOGE_LIST.add("Sample");
}
もう登場させてしまったが、今回は Map でもインスタンスイニシャライザが使えるよーというサンプルを見せつつ、要素の追加や変更ができないコレクションを作る方法をまとめる。
ちなみに、以前 List の場合でインスタンスイニシャライザを紹介している。
インスタンスイニシャライザと Collections の併用
早速コードで紹介。
// インスタンスイニシャライザを使ったリスト。final を付けているが後から add() ができてしまう
private static final List<String> LIST_1 = new ArrayList<String>() {{ add("FUGA"); add("PIYO"); }};
// Collections.unmodifiableList() を使うと、追加や削除ができないリストになる
private static final List<String> LIST_2 = Collections.unmodifiableList( new ArrayList<String>() {{ add("FUGA"); add("PIYO"); }} );
// インスタンスイニシャライザを使ったマップ。サクッと初期値がセットできているが、LIST_1 と同じく変更が効いてしまう
private static final Map<Integer, String> MAP_1 = new HashMap<Integer, String>() {{ put(1, "Hoge"); put(2, "Fuga"); }};
// こちらも Collections.unmodifiableMap() を使うと、追加や削除ができないマップになる
private static final Map<Integer, String> MAP_2 = Collections.unmodifiableMap( new HashMap<Integer, String>() {{ put(1, "Hoge"); put(2, "Fuga"); }} );
Collections.unmodifiable
なんたら、なメソッドが、コレクションの型に合わせていくつか存在している。その引数にコレクションを渡すことで、add()
や put()
した時に UnsupportedOperationException
が throw される、追加や削除ができないコレクションを生成してくれる (ところでどうして unmodifiable
系のメソッドはオーバーロードで作らなかったんだろう?)。
ここまでやってあげれば、意図した「final な動き」になってくれる。
- 参考 : コレクションの初期化にはスタティックイニシャライザではなくインスタンスイニシャライザを使用する - C/pHeR Memo - Java とか。Eclipse とか。
- 参考 : static finalなコレクションの生成について - suusuke@Hatena
- 参考 : Map型のインスタンス変数やクラス変数に"格好良く"初期値を与える方法(1) | risaiku リサイク
- 参考 : Javaでデータの入ったMapをクールに初期化する方法 | もっとクールにプログラミング
配列はどうしても中身が変更できてしまう
配列の場合は、array[1] = "New Value";
みたいなことを避ける方法がないようだ。諦めて Collections.unmodifiableList()
を使おう。