JavaでSingletonパターンを実装するのにclassとenumのどちらを使うべきか
Java6でSingletonパターンを実装する方法を勉強した。
JavaでSingletonを実装する方法は大きく分けてclassを使う方法とenumを使う方法の2通りが存在する。
classだとシリアライズ・デシリアライズの際にインスタンスの唯一性を保つためにはあれこれと細かい設定が必要になる。
一方で、enum型を利用するとそういった煩雑さから解放される。
classを利用する場合
classを利用する場合、実装方法は大きく分けて2通り
1. public static finalなフィールドでインスタンスを管理
class Singleton { // finalにすることで唯一のインスタンスを上書きできないようにする public static final INSTANCE = new Singleton(); // 単なるフィールド private String field; // コンストラクタをprivateにすることでクラス外でのインスタンス化が不可能にする private Singleton() {} public String getField() { return field; } public void setField(String field) { this.field = field; } }
2. privateなフィールドでインスタンスを管理しfactoryメソッドでインスタンスを提供
class Singleton { // privateフィールドを利用 private static Singleton INSTANCE = new Singleton(); // 単なるフィールド private String field; // コンストラクタをprivateにすることでクラス外でのインスタンス化が不可能にする private Singleton() {} // インスタンスを提供するfactoryメソッド public static Singleton getInstance() { return INSTANCE; } public String getField() { return field; } public void setField(String field) { this.field = field; } }
しかしながら、これらをシリアライズ可能にするには細心の注意が必要。
1. public static finalなフィールドでインスタンスを管理(シリアライズ可能版)
class Singleton implements Serializable { // finalにすることで唯一のインスタンスを上書きできないようにする public static final INSTANCE = new Singleton(); // 全てのフィールドはtransientにする必要がある private transient String field; // コンストラクタをprivateにすることでクラス外でのインスタンス化が不可能にする private Singleton() {} // readObjectメソッドの内容を破棄する private Object readResolve() throws ObjectStreamException { return INSTANCE; } public String getField() { return field; } public void setField(String field) { this.field = field; } }
2. privateなフィールドでインスタンスを管理しfactoryメソッドでインスタンスを提供(シリアライズ可能版)
class Singleton implements Serializable { // privateフィールドを利用 private static Singleton INSTANCE = new Singleton(); // 全てのフィールドはtransientにする必要がある private transient String field; // コンストラクタをprivateにすることでクラス外でのインスタンス化が不可能にする private Singleton() {} // インスタンスを提供するfactoryメソッド public static Singleton getInstance() { return INSTANCE; } // readObjectメソッドの内容を破棄する private Object readResolve() throws ObjectStreamException { return INSTANCE; } public String getField() { return field; } public void setField(String field) { this.field = field; } }
このようにclassベースでSingletonパターンをバグなく実装するのは結構煩雑な作業を要する。
そこで、最近ではenum型を利用した実装法が推奨されるようになっている。
enum型を利用
enum Singleton { INSTANCE; private String field; public String getField() { return field; } public void setField(String field) { this.field = field; } }
enum型を用いると宣言された定数の他にインスタンスが存在しないことをJVMが保証してくれることになる。
また、シリアライズ・デシリアライズに関してややこしいことをする必要もない。
結論
Singletonを実装するにはenum型を使用すべき。ただ、シリアライズ可能でかつenum型を使えない場合は、注意してreadResolveメソッドを提供し、かつ全ての参照を保持するインスタンスフィールドをtransientにする必要がある。
参考
Effective Java 第2版 (The Java Series)
- 作者: Joshua Bloch,柴田芳樹
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2008/11/27
- メディア: 単行本(ソフトカバー)
- 購入: 54人 クリック: 706回
- この商品を含むブログ (245件) を見る