risposta-alla-domanda-sullo-sviluppo-web-bd.com

Implementazione di Singleton con un Enum (in Java)

Ho letto che è possibile implementare Singleton in Java usando un Enum come:

public enum MySingleton {
     INSTANCE;   
}

Ma come funziona il sopra? Nello specifico, un Object deve essere istanziato. Qui, come viene istanziata MySingleton? Chi sta facendo new MySingleton()?

150
Anand

Questo,

public enum MySingleton {
  INSTANCE;   
}

ha un costruttore implicito vuoto. Rendilo esplicito invece,

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

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

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

Vedresti

Here
INSTANCE

I campi enum sono costanti di tempo di compilazione, ma sono istanze del loro tipo enum. E, sono costruiti quando il tipo enum è referenziato per la prima volta .

182
Elliott Frisch

Un tipo enum è un tipo speciale di class.

Il tuo enum verrà in realtà compilato per qualcosa di simile

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

Quando il tuo codice accede per la prima volta a INSTANCE, la classe MySingleton verrà caricata e inizializzata dalla JVM. Questo processo inizializza il campo static sopra una volta (pigramente).

67

In questo libro delle migliori pratiche di Java di Joshua Bloch, puoi trovare spiegazioni sul perché dovresti applicare la proprietà Singleton con un costruttore privato o un tipo Enum. Il capitolo è piuttosto lungo, quindi mantenendolo riassunto:

Rendere una classe un Singleton può rendere difficile testare i suoi clienti, poiché è impossibile sostituire un'implementazione simulata per un singleton a meno che non implementi un'interfaccia che funge da suo 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 del campo pubblico, tranne che è più conciso, fornisce il meccanismo di serializzazione gratuito e fornisce una garanzia ferrea contro l'istanziazione multipla, anche di fronte a sofisticati attacchi di serializzazione o riflessione.

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

57
ekostadinov

Come tutte le istanze di enum, Java istanzia ogni oggetto quando la classe viene caricata, con la garanzia che viene istanziato esattamente una volta per JVM . Pensa alla dichiarazione INSTANCE come un campo finale statico pubblico: Java creerà un'istanza dell'oggetto al primo riferimento alla classe.

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

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

9
Jeff Bowman

Poiché Singleton Pattern riguarda l'avere un costruttore privato e il richiamo di un metodo per controllare le istanze (come alcuni getInstance), in Enums abbiamo già un costruttore privato implicito .

Non so esattamente come JVM o alcuni contenitore controlla le istanze del nostro Enums, ma sembra che usi già un Singleton Pattern implicito, la differenza è che non chiamiamo getInstance, chiamiamo semplicemente Enum.

4
Bruno Franco

Come è già stato detto in precedenza, un enum è una classe Java con la condizione speciale che la sua definizione debba iniziare con almeno una "costante enum".

In parte, è una classe come qualsiasi classe e la si utilizza aggiungendo metodi al di sotto delle definizioni costanti:

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

Si accede 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'istanziazione sicura del thread del singleton e una garanzia che sarà sempre una sola copia.

E, forse, cosa più importante, che questo comportamento è garantito dalla JVM stessa e dalle specifiche Java.

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

Un tipo enum non ha altre istanze oltre a quelle definite dalle sue costanti enum. È un errore in fase di compilazione per tentare di istanziare esplicitamente un tipo di enum. Il metodo clone finale in Enum garantisce che le costanti enum non possano mai essere clonate e il trattamento speciale mediante il meccanismo di serializzazione garantisce che le istanze duplicate non vengano mai create a seguito della deserializzazione. L'istanziazione riflessiva dei tipi di enum è proibita. 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'istanziazione tutti i problemi relativi alla sicurezza dei thread devono essere gestiti come in qualsiasi altra classe con la parola chiave sincronizzata, ecc.

2
Erk