Enumを使用したシングルトンの実装(Java)


178

私は次のようなものをSingleton使用してJavaで実装することが可能であることを読みEnumました:

public enum MySingleton {
     INSTANCE;   
}

しかし、上記はどのように機能しますか?具体的には、Objectインスタンス化する必要があります。ここでは、どのようMySingletonにインスタンス化されていますか?誰がやってるのnew MySingleton()


24
新しいMySingleton()を実行しているのはJVMです。
Sotirios Delimanolis 2014年

37
INSTANCEと同じpublic static final MySingleton INSTANCE = new MySingleton();です。
Pshemo 2014年

6
ENUM-保証されたシングルトン。
バグではない

dzone.com/articles/java-singletons-using-enumを読んでください 。なぜenumを使用し、他のメソッドは使用しないのですか。ショート:シリアル化とリフレクションを使用すると問題が発生します。
宮城県

回答:


203

この、

public enum MySingleton {
  INSTANCE;   
}

暗黙的な空のコンストラクタがあります。代わりに明示的にしてください。

public enum MySingleton {
    INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

次に、次のmain()ようなメソッドで別のクラスを追加した場合

public static void main(String[] args) {
    System.out.println(MySingleton.INSTANCE);
}

あなたは見るでしょう

Here
INSTANCE

enumフィールドはコンパイル時の定数ですが、それらはそのenum型のインスタンスです。そして、列挙型が初めて参照されるときに構築されます。


13
デフォルト列挙型で暗黙のプライベートコンストラクタを持っているし、あなたが実際にあなたがそのコンストラクタで実行する必要があること、コードを持っていない限り、明示的にプライベートコンストラクタを追加することが必要とされていないことをことを追加する必要があります
ニムロッドダヤン

各列挙フィールドはインスタンスを1回だけ作成するため、プライベートコンストラクタを作成する必要はありません。
Pravat Panda 2016

2
パブリック列挙型MySingleton {INSTANCE、INSTANCE1; 次に、System.out.println(MySingleton.INSTANCE.hashCode()); System.out.println(MySingleton.INSTANCE1.hashCode()); 異なるハッシュコードを出力します。MySingletonの2つのオブジェクトが作成されるということですか?
スコットマイル

@scottmilesはい。2つのインスタンスがあるためです。シングルトンには、定義により1つあります。
Elliott Frisch 2016年

private修飾子は、のために意味を持たないenumコンストラクタと、完全に冗長です。
Dmitri Nesteruk 2017年

76

enumタイプはの特殊なタイプですclass

あなたenumは実際に次のようなものにコンパイルされます

public final class MySingleton {
    public final static MySingleton INSTANCE = new MySingleton();
    private MySingleton(){} 
}

コードが最初ににアクセスするINSTANCEと、クラスMySingletonがロードされ、JVMによって初期化されます。このプロセスは、static上記のフィールドを1回(遅延的に)初期化します。


このクラスには、プライベートのコンストラクタも含まれますか?そうだと思います
Yasir Shabbir Choudhary

public enum MySingleton { INSTANCE,INSTANCE1; }そして、System.out.println(MySingleton.INSTANCE.hashCode()); System.out.println(MySingleton.INSTANCE1.hashCode());異なるハッシュコードを出力します。MySingletonの2つのオブジェクトが作成されるということですか?
スコットマイル

2
@scottmilesはい、これは2つの差のenum定数です。
Sotirios Delimanolis 16年

2

@SotiriosDelimanolis、列挙型スレッドを使用したこのアプローチは安全ですか?
abg

63

Joshua BlochによるこのJavaのベストプラクティスブックでは、プライベートコンストラクターまたはEnum型でシングルトンプロパティを適用する必要がある理由を説明しています。この章は非常に長いため、要約しておく必要があります。

クラスをシングルトンにすると、そのタイプとして機能するインターフェースを実装しない限り、シングルトンをモック実装に置き換えることができないため、クライアントをテストすることが難しくなる可能性があります。推奨されるアプローチは、1つの要素で列挙型を作成するだけでシングルトンを実装することです。

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

このアプローチは、機能がパブリックフィールドアプローチと同等ですが、より簡潔であり、シリアル化機構を無料で提供し、洗練されたシリアル化またはリフレクション攻撃に直面しても、複数のインスタンス化に対して強力な保証を提供します。

このアプローチはまだ広く採用されていませんが、単一要素の列挙型がシングルトンを実装するための最良の方法です。


シングルトンをテスト可能にするには、戦略パターンを使用して、すべてのシングルトンのアクションに戦略を実行させることができると思います。したがって、1つの「本番」戦略と1つの「テスト」戦略を持つことができます...
Erk

9

すべての列挙型インスタンスと同様に、Javaはクラスが読み込まれるときに各オブジェクトをインスタンス化しますが、JVMごとに1回だけインスタンス化されることが保証されています。INSTANCE宣言をpublic static finalフィールドと考えてください。Javaは、クラスが初めて参照されるときにオブジェクトをインスタンス化します。

インスタンスは、Java言語仕様のセクション12.4で定義されている静的初期化中に作成されます。

その価値については、Joshua Blochがこのパターンを「Effective Java Second Edition」の項目3として詳しく説明しています。


6

以来Singletonパターンはいくつかのように(プライベートコンストラクタを持つとインスタンス化を制御するいくつかのメソッドを呼び出すことについてですgetInstance)、列挙型では我々はすでに暗黙のプライベートコンストラクタを持っています。

JVMまたは一部のコンテナがのインスタンスをどのよう制御するかは正確にはわかりませんEnumsが、それはすでに暗黙のを使用しているようです。Singleton Pattern違いはgetInstance、を呼び出さず、Enumを呼び出すだけです。


3

以前にある程度言及したように、列挙型は、その定義が少なくとも1つの「列挙型定数」で始まる必要があるという特別な条件を持つJavaクラスです。

それとは別に、列挙型を拡張したり他のクラスを拡張するために使用したりすることはできません。列挙型は他のクラスと同様のクラスであり、定数定義の下にメソッドを追加して使用します。

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

次のようにシングルトンのメソッドにアクセスします。

MySingleton.INSTANCE.doSomething();
String something = MySingleton.INSTANCE.getSomething();

クラスの代わりに列挙型を使用することは、他の回答で述べたように、主にシングルトンのスレッドセーフなインスタンス化と、常に1つのコピーだけになることの保証についてです。

そして、おそらく最も重要なこととして、この動作はJVM自体とJava仕様によって保証されています。

enumインスタンスの複数のインスタンスがどのように防止されるかについてのJava仕様のセクションを次に示します。

列挙型には、その列挙定数で定義されたもの以外のインスタンスはありません。列挙型を明示的にインスタンス化しようとすると、コンパイル時エラーになります。Enumの最後のcloneメソッドは、enum定数が複製されないようにし、シリアル化メカニズムによる特別な処理により、逆シリアル化の結果として重複インスタンスが作成されないようにします。列挙型の反射的なインスタンス化は禁止されています。これらの4つの要素により、列挙型のインスタンスが列挙型定数で定義されたインスタンスを超えて存在しないことが保証されます。

注目に値するのは、インスタンス化後、スレッドの安全性に関する懸念は、他のクラスと同様に、synchronizedキーワードなどで処理する必要があることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.