cypher256's blog

Pleiades とか作った

コレクションの初期化にはスタティックイニシャライザではなくインスタンスイニシャライザを使用する

Java で static final な配列を public で公開すると要素が書き換えられてしまうため、コピーを返せというイディオムがあります(書籍 Effective Java - 項目 12)。要は不変(イミュータブル)でないオブジェクトを static final で公開するというのはほとんどの場合、誤りだから気をつけてね的なものです。

static final な配列

// 悪い例
public static final String[] VALUES = {"要素1", "要素2"};

// 良い例
private static final String[] VALUES = {"要素1", "要素2"};
public static String[] getValues() {
    return VALUES.clone();
}

このイディオムは Java の言語仕様上、配列要素の参照を不変にできないため、仕方なくコピーを返すものです。目的がコピーして内容を書き換えて再利用するのなら良いのですが、コレクションを static final で使用するときも、コピーして返すのが良いというのをたまにみかけます。それならば、下記に示すようにスタティックイニシャライザで不変化するほうがまだましです。この方法は static ブロックによりクラスロード時にクラスがロックされるため、スレッドセーフです。

static final なコレクション - スタティックイニシャライザを使用(冗長)

public static final List<String> LIST;
static {
    List<String> tmp = new LinkedList<String>();
    tmp.add("要素1");
    tmp.add("要素2");
    LIST = Collections.unmodifiableList(tmp);
}

上記のように、通常クラスの初期化にはスタティックイニシャライザが使用されます。ただ、final の場合、一度しか代入できないため tmp を一時的に使用していますが、何か冗長ですし、スマートではありません。より良い方法としては下記に示すように、匿名クラスとインスタンスイニシャライザを使用する方法があります。

static final なコレクション - 匿名クラスとインスタンスイニシャライザを使用

public static final List<String> LIST = Collections.unmodifiableList(new LinkedList<String>(){{
    add("要素1");
    add("要素2");
}});

通常、インスタンスイニシャライザはほとんど使われることがありません。なぜならば、スタティックイニシャライザがクラスを初期化する唯一の方法なのに対し、通常、インスタンスの初期化はコンストラクタで事足りるからです。しかし、匿名クラスではコンストラクタを記述できないため、インスタンスイニシャライザを使用します。ちなみにマップの場合も書き方はほぼ同様で Collections.unmodifiableMap を使用するだけです。システム共通の定数リストや定数マップを使う機会がある場合はぜひ使ってみてください。


上で紹介している書籍 Effective Java はライブラリー担当者や標準化チーム、フレームワーク作成に携わる方に特にお勧めです。私が購入したのは 6 年前で、今となっては内容的に古いですが、内容よりも Java のコレクションフレームワークを設計・実装した Joshua Bloch の思想が良く分かり面白いです。Joshua Bloch は java.util.Map や List、最近ではアノテーションソースコードの @author に名前があります。