2.5. 配列フィールド定数
『目的』
定数として用意した配列は書きかえられないようにしましょう。
『Before』
定数はpublic static final宣言だからということでこのように書いてみます。
public static final int[] ARRAY_HOGE = {0, 1, 5, 10};
finalとしているので一見不変に思えるのですが、落とし穴があります。
長さがゼロ「ではない」配列は常に可変なのです。
final修飾子はあくまでも参照を不変にするのであって中身を不変とするわけではありません。
ARRAY_HOGE[0] = 100;
/* 以後、書き換えられたまま */
System.out.println ("ARRAY_HOGE[0]=" + ARRAY_HOGE[0]);
定数が書き換えられてしまいます。
出力結果は0ではなく100ですね。
『After』
privateな配列とpublicな不変なリストを用意してみます。
/* 実態は内部に隠蔽しておいて */
private static final int[] PRIVATE_ARRAY_HOGE = {0, 1, 5, 10};
/* 不変にした状態で外部に公開 */
public static final List ARRAY_HOGE =
Collections.unmodifiableList(Arrays.asList(PRIVATE_ARRAY_HOGE));
よさそうなのですが、int[]からListになってしまいますね。
コンパイル時の「型の安全性」が失われてしまいます。
(型の安全性 = コンパイルが通れば実行時に型エラーが絶対に起こらない)
Listからintを取り出すときにキャストを忘れているとClassCastExceptionが起きてしまいます。
型を維持した書き方にしましょう。
/* 実態は内部に隠蔽しておいて */
private static final int[] PRIVATE_ARRAY_HOGE = {0, 1, 5, 10};
/* 不変にした状態で外部に公開するメソッド。 */
public static final int[] hoge() {
return (int[]) PRIVATE_ARRAY_HOGE.clone();
}
これで型の安全性を維持できます。
ただしトレードオフとしてパフォーマンスの低下という代償がありますし、
clone() がきちんと実装されている必要があります。
『まとめ』
書き方を改めることで「定数」となります。
final修飾子は参照を不変にするだけで内容までは不変にしてくれません。