はじめに
Java では final の配列の要素は可変です。私が何を言っているかわからない方は下記のコードを実行してみてください。
public class Main {
private static final String[] STRINGS = {"Tomato", "Orange", "Apple"};
public static void main(final String[] args) {
STRINGS[2] = "Cabbage";
System.out.println(Arrays.toString(STRINGS));
}
}
上記のコードを実行した際の出力は下記の通りです。
[Tomato, Orange, Cabbage]
大変です! Apple が Cabbage に置き換わってしまいました。
では、固定の要素集合を提供するには?
『Effective Java』(日本語版第2版p70)にも書いてある通り、「変更できない要素の集合」を定義したいなら下記の方法を使いましょう。
Collections#unmodifiableList
引数で渡された List と同じ要素を持つ、変更不可の List を返すメソッドです。下記の例では Arrays#asList で一時的な List を作り、それを引数にして Collections#unmodifiableList を呼び出し、変更不可の List を取得しています。
private static final List<String> STRINGS
= Collections.unmodifiableList(Arrays.asList("Tomato", "Orange", "Apple"));
実装はこちらのリンクをご参照ください。
ちなみに、Arrays#asList で生成される List は add と remove ができませんが、 set は可能です。
UnmodifiableCollection のよくないところ
ここから本題です。せっかく unmodifiableList を取得しても、Listの実装である以上は要素変更のメソッド(add/remove/set)が存在していますので、下記のコードは正常にコンパイル可能です。
STRINGS.set(2, "Cabbage");
が、実行すると下記の通り UnsupportedOperationException となります。
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableList.set(Unknown Source)
at jp.toastkid.Main.main(Main.java:13)
うーん……継承を使って実装する以上は仕方ないという感じですかね……しかし、実行時に例外を出されてプログラムを止められるよりは、コーディング中にコンパイルエラーを出してくれた方がいいと私は思います。
そこで
可能なら Eclipse Collections の ImmutableCollection を使ってみるとよいでしょう。元から add/remove/set が実装されていないので、間違って実行する以前にコンパイルエラーとなります。
例
下記の例では3つの要素を持つ ImmutableList のインスタンスを生成します。そのインスタンスに対し set メソッドを呼び出そうとしますが、そのようなメソッドは ImmutableList には実装されていないため、コンパイルが通りません。
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.impl.factory.Lists;
......
private static final ImmutableList<String> STRINGS
= Lists.immutable.of("Tomato", "Orange", "Apple");
public static void main(final String[] args) {
STRINGS.set(2, "Cabbage"); // ここでコンパイルエラー
System.out.println(STRINGS);
}
変更不可の状態を保てなくはなりますが、toList() メソッドで java.util.List への変換も可能です。
参考
- java.util.Collections
- java.util.Arrays
- org.eclipse.collections.api.collection.ImmutableCollection
- GS Collections 使い方メモ……GS Collections は Eclipse Collections の旧称です。