Implementazione di Singleton con un Enum (in Java)


178

Ho letto che è possibile implementare Singletonin Java usando Enumcome:

public enum MySingleton {
     INSTANCE;   
}

Ma come funziona quanto sopra? In particolare, Objectè necessario creare un'istanza. Qui, come viene MySingletonistanziata? Chi sta facendo new MySingleton()?


24
Chi sta facendo il nuovo MySingleton () La JVM lo è.
Sotirios Delimanolis,

37
INSTANCEè lo stesso di public static final MySingleton INSTANCE = new MySingleton();.
Pshemo,

6
ENUM - Un singleton garantito.
Nessun errore

Leggi dzone.com/articles/java-singletons-using-enum , perché usare enum e non gli altri metodi. Breve: i problemi iniziano quando si usano la serializzazione e la riflessione.
Miyagi,

Risposte:


203

Questo,

public enum MySingleton {
  INSTANCE;   
}

ha un costruttore vuoto implicito. Rendilo esplicito invece,

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

Se poi hai aggiunto un'altra classe con un main()metodo simile

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

Vedresti

Here
INSTANCE

enumi campi sono costanti di tempo di compilazione, ma sono istanze del loro enumtipo. Inoltre, sono costruiti quando si fa riferimento al tipo enum per la prima volta .


13
Dovresti aggiungere che per impostazione predefinita gli enum hanno un costruttore privato implicito e che l'aggiunta esplicita di un costruttore privato non è necessaria a meno che tu non abbia effettivamente il codice che devi eseguire in quel costruttore
Nimrod Dayan,

ogni campo enum crea un'istanza una sola volta, quindi non è necessario creare un costruttore privato.
Pravat Panda,

2
enum pubblico MySingleton {INSTANCE, INSTANCE1; } e quindi System.out.println (MySingleton.INSTANCE.hashCode ()); System.out.println (MySingleton.INSTANCE1.hashCode ()); stampa diversi hashcode. Significa che vengono creati due oggetti di MySingleton?
miglia scott

@scottmiles Sì. Perché hai due casi. Un singleton, per definizione, ne ha uno.
Elliott Frisch,

Il privatemodificatore non ha alcun significato per un enumcostruttore ed è completamente ridondante.
Dmitri Nesteruk,

76

Un enumtipo è un tipo speciale di class.

Il tuo enumsarà effettivamente compilato in qualcosa del genere

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

Al primo accesso al codice INSTANCE, la classe MySingletonverrà caricata e inizializzata da JVM. Questo processo inizializza il staticcampo sopra una volta (pigramente).


Questa classe conterrà anche il costruttore privato ()? Penso che sarebbe
Yasir Shabbir Choudhary,

public enum MySingleton { INSTANCE,INSTANCE1; }e quindi System.out.println(MySingleton.INSTANCE.hashCode()); System.out.println(MySingleton.INSTANCE1.hashCode());stampa diversi hashcode. Significa che vengono creati due oggetti di MySingleton?
miglia scott

2
@scottmiles Sì, sono solo due enumcostanti di differenza .
Sotirios Delimanolis,


@SotiriosDelimanolis, questo approccio con enum thread è sicuro?
abg

63

In questo libro sulle migliori pratiche Java di Joshua Bloch, puoi scoprire perché dovresti applicare la proprietà Singleton con un costruttore privato o un tipo Enum. Il capitolo è piuttosto lungo, quindi tenendolo in sintesi:

Rendere una classe un Singleton può rendere difficile testare i suoi clienti, poiché è impossibile sostituire un'implementazione simulata con un singleton a meno che non implementi un'interfaccia che funge da tipo. L'approccio consigliato è implementare Singletons semplicemente creando un tipo enum con un elemento:

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

Questo approccio è funzionalmente equivalente all'approccio di campo pubblico, tranne per il fatto che è più conciso, fornisce gratuitamente i macchinari di serializzazione e fornisce una garanzia coraggiosa contro l'istanza multipla, anche di fronte a sofisticati attacchi di serializzazione o di riflessione.

Mentre questo approccio deve ancora essere ampiamente adottato, un tipo enum a elemento singolo è il modo migliore per implementare un singleton.


Penso, per rendere testabile un singleton, potresti usare un modello di strategia e far passare tutte le azioni del singleton attraverso la strategia. Quindi puoi avere una strategia di "produzione" e una di "test" ...
Erk,

9

Come tutte le istanze enum, Java crea un'istanza per ogni oggetto quando la classe viene caricata, con una certa garanzia che viene istanziata esattamente una volta per JVM . Pensa alla INSTANCEdichiarazione come a un campo finale statico pubblico: Java creerà un'istanza dell'oggetto la prima volta che si fa riferimento alla classe.

Le istanze vengono create durante l'inizializzazione statica, definita nella specifica del linguaggio Java, sezione 12.4 .

Per quello che vale, Joshua Bloch descrive questo modello in dettaglio come elemento 3 di Effective Java Second Edition .


6

Dato che Singleton Pattern riguarda avere un costruttore privato e chiamare un metodo per controllare le istanze (come alcuni getInstance), in Enums abbiamo già un costruttore privato implicito.

Non so esattamente come la JVM o alcuni container controllino le istanze della nostra Enums, ma sembra che usi già un implicito Singleton Pattern, la differenza è che non chiamiamo a getInstance, chiamiamo semplicemente Enum.


3

Come è stato precedentemente menzionato, un enum è una classe java con la condizione speciale che la sua definizione debba iniziare con almeno una "costante enum".

A parte questo, e che gli enum non possono essere estesi o usati per estendere altre classi, un enum è una classe come qualsiasi classe e la si usa aggiungendo metodi sotto le definizioni costanti:

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

Accedete ai metodi del singleton seguendo queste linee:

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

L'uso di un enum, invece di una classe, è, come è stato detto in altre risposte, principalmente su un'istanza thread-safe del singleton e una garanzia che sarà sempre solo una copia.

E, forse, soprattutto, che questo comportamento è garantito dalla stessa JVM e dalle specifiche Java.

Ecco una sezione dalla specifica Java su come vengono impedite più istanze di un'istanza enum:

Un tipo enum non ha istanze diverse da quelle definite dalle sue costanti enum. È un errore in fase di compilazione tentare di creare un'istanza esplicita di un tipo enum. Il metodo di clonazione finale in Enum assicura che le costanti di enum non possano mai essere clonate, e il trattamento speciale con il meccanismo di serializzazione garantisce che istanze duplicate non vengano mai create come risultato della deserializzazione. È vietata l'istanza riflessiva dei tipi di enum. Insieme, queste quattro cose assicurano che non esistano istanze di un tipo enum oltre a quelle definite dalle costanti enum.

Vale la pena notare che dopo l'istanza tutte le preoccupazioni relative alla sicurezza dei thread devono essere gestite come in qualsiasi altra classe con la parola chiave sincronizzata ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.