Implémentation de Singleton avec un Enum (en Java)

178

J'ai lu qu'il est possible de mettre Singletonen œuvre en Java en utilisant un Enumtel que:

public enum MySingleton {
     INSTANCE;   
}

Mais comment cela fonctionne-t-il? Plus précisément, un Objectdoit être instancié. Ici, comment l' MySingletoninstanciation? Qui fait new MySingleton()?

Anand
la source
24
Qui fait le nouveau MySingleton () La JVM est.
Sotirios Delimanolis
37
INSTANCEest le même que public static final MySingleton INSTANCE = new MySingleton();.
Pshemo
6
ENUM - Un singleton garanti.
Pas un bug
Lisez dzone.com/articles/java-singletons-using-enum , pourquoi utiliser enum et non les autres méthodes. Bref: les problèmes commencent lors de l'utilisation de la sérialisation et de la réflexion.
Miyagi

Réponses:

203

Ce,

public enum MySingleton {
  INSTANCE;   
}

a un constructeur vide implicite. Rendez-le plutôt explicite,

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

Si vous avez ensuite ajouté une autre classe avec une main()méthode comme

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

Tu verrais

Here
INSTANCE

enumLes champs sont des constantes de temps de compilation, mais ce sont des instances de leur enumtype. Et, ils sont construits lorsque le type enum est référencé pour la première fois .

Elliott Frisch
la source
13
Vous devriez ajouter que, par défaut, les énumérations ont un constructeur privé implicite et que l'ajout explicite d'un constructeur privé n'est pas nécessaire à moins que vous n'ayez réellement du code que vous devez exécuter dans ce constructeur
Nimrod Dayan
chaque champ enum crée une instance une seule fois, donc pas besoin de créer un constructeur privé.
Pravat Panda le
2
public enum MySingleton {INSTANCE, INSTANCE1; } puis System.out.println (MySingleton.INSTANCE.hashCode ()); System.out.println (MySingleton.INSTANCE1.hashCode ()); il imprime différents hashcodes. Cela signifie-t-il que deux objets de MySingleton sont créés?
Scott Miles
@scottmiles Oui. Parce que vous avez deux instances. Un singleton, par définition, en a un.
Elliott Frisch
Le privatemodificateur n'a aucune signification pour un enumconstructeur et est entièrement redondant.
Dmitri Nesteruk
76

Un enumtype est un type spécial de class.

Votre enumsera en fait compilé en quelque chose comme

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

Lorsque votre code accède pour la première fois INSTANCE, la classe MySingletonest chargée et initialisée par la JVM. Ce processus initialise le staticchamp ci-dessus une fois (paresseusement).

Sotirios Delimanolis
la source
Cette classe contient-elle également un constructeur privé ()? Je pense que ce serait
Yasir Shabbir Choudhary
public enum MySingleton { INSTANCE,INSTANCE1; }puis System.out.println(MySingleton.INSTANCE.hashCode()); System.out.println(MySingleton.INSTANCE1.hashCode());il imprime différents hashcodes. Cela signifie-t-il que deux objets de MySingleton sont créés?
scott miles
2
@scottmiles Oui, ce ne sont que deux enumconstantes de différence .
Sotirios Delimanolis
2
@caf, bien sûr. Voir ici: docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3
Sotirios Delimanolis
@SotiriosDelimanolis, cette approche avec enum thread-safe est-elle sûre?
abg
63

Dans ce livre de bonnes pratiques Java de Joshua Bloch, vous pouvez découvrir pourquoi vous devez appliquer la propriété Singleton avec un constructeur privé ou un type Enum. Le chapitre est assez long, alors gardez-le résumé:

Faire d'une classe un singleton peut rendre difficile le test de ses clients, car il est impossible de remplacer une implémentation fictive pour un singleton à moins qu'il n'implémente une interface qui lui sert de type. L'approche recommandée consiste à implémenter des singletons en créant simplement un type enum avec un élément:

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

Cette approche est fonctionnellement équivalente à l'approche du champ public, sauf qu'elle est plus concise, fournit gratuitement la machinerie de sérialisation et offre une garantie absolue contre l'instanciation multiple, même face à des attaques de sérialisation ou de réflexion sophistiquées.

Bien que cette approche n'ait pas encore été largement adoptée, un type enum à un seul élément est le meilleur moyen d'implémenter un singleton.

ekostadinov
la source
Je pense que pour rendre un singleton testable, vous pouvez utiliser un modèle de stratégie et faire passer toutes les actions du singleton par la stratégie. Vous pouvez donc avoir une stratégie de "production" et une stratégie de "test" ...
Erk
9

Comme toutes les instances enum, Java instancie chaque objet lorsque la classe est chargée, avec une certaine garantie qu'elle est instanciée exactement une fois par JVM . Considérez la INSTANCEdéclaration comme un champ final statique public: Java instanciera l'objet la première fois que la classe sera référencée.

Les instances sont créées lors de l'initialisation statique, qui est définie dans la spécification du langage Java, section 12.4 .

Pour ce que cela vaut, Joshua Bloch décrit ce modèle en détail comme l'élément 3 de Effective Java Second Edition .

Jeff Bowman
la source
6

Puisque Singleton Pattern consiste à avoir un constructeur privé et à appeler une méthode pour contrôler les instanciations (comme certaines getInstance), dans Enums, nous avons déjà un constructeur privé implicite.

Je ne sais pas exactement comment la JVM ou un conteneur contrôle les instances de notre Enums, mais il semble qu'il utilise déjà un implicite Singleton Pattern, la différence est que nous n'appelons pas a getInstance, nous appelons simplement Enum.

Bruno Franco
la source
3

Comme cela a, dans une certaine mesure, été mentionné précédemment, une enum est une classe java avec la condition spéciale que sa définition doit commencer par au moins une "constante enum".

En dehors de cela, et que les énumérations ne peuvent pas être étendues ou utilisées pour étendre d'autres classes, une énumération est une classe comme n'importe quelle classe et vous l'utilisez en ajoutant des méthodes sous les définitions de constante:

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

Vous accédez aux méthodes du singleton en suivant ces lignes:

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

L'utilisation d'une énumération, au lieu d'une classe, est, comme cela a été mentionné dans d'autres réponses, principalement sur une instanciation thread-safe du singleton et une garantie qu'il ne sera toujours qu'une copie.

Et, peut-être, plus important encore, que ce comportement est garanti par la machine virtuelle Java elle-même et la spécification Java.

Voici une section de la spécification Java sur la façon dont plusieurs instances d'une instance enum sont empêchées:

Un type enum n'a pas d'instances autres que celles définies par ses constantes enum. La tentative d'instanciation explicite d'un type enum est une erreur de compilation. La méthode de clonage finale dans Enum garantit que les constantes d'énumération ne peuvent jamais être clonées, et le traitement spécial par le mécanisme de sérialisation garantit que les instances dupliquées ne sont jamais créées à la suite de la désérialisation. L'instanciation réfléchissante des types enum est interdite. Ensemble, ces quatre éléments garantissent qu'aucune instance d'un type enum n'existe au-delà de celles définies par les constantes enum.

Il convient de noter qu'après l'instanciation, tout problème de sécurité des threads doit être traité comme dans toute autre classe avec le mot-clé synchronized, etc.

Erk
la source