J'ai lu qu'il est possible d'implémenter Singleton
dans Java en utilisant un Enum
tel que:
public enum MySingleton {
INSTANCE;
}
Mais comment fonctionne ce qui précède? Plus précisément, une Object
doit être instanciée. Ici, comment MySingleton
est-il instancié? Qui fait new MySingleton()
?
Cette,
public enum MySingleton {
INSTANCE;
}
a un constructeur vide implicite. Rendez-le explicite à la place,
public enum MySingleton {
INSTANCE;
private MySingleton() {
System.out.println("Here");
}
}
Si vous avez ensuite ajouté une autre classe avec une méthode main()
comme
public static void main(String[] args) {
System.out.println(MySingleton.INSTANCE);
}
Tu verrais
Here
INSTANCE
Les champs enum
sont des constantes de temps de compilation, mais ce sont des instances de leur type enum
. Et, ils sont construits lorsque le type enum est référencé pour la première fois.
Un type enum
est un type spécial de class
.
Votre enum
sera 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 MySingleton
sera chargée et initialisée par la JVM. Ce processus initialise le champ static
ci-dessus ne fois (paresseusement).
Dans ce livre de bonnes pratiques Java de Joshua Bloch, vous pouvez trouver une explication de la raison pour laquelle vous devez appliquer la propriété Singleton avec un constructeur privé ou un type Enum. Le chapitre est assez long, alors résumons-le:
Faire d’une classe un singleton peut rendre difficile le test de ses clients, car il est impossible de remplacer une implémentation factice par un singleton à moins de mettre en œuvre une interface qui en est le type. L’approche recommandée consiste à implémenter 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, à la différence qu’elle est plus concise, fournit le mécanisme de sérialisation gratuitement et offre une garantie absolue contre toute instanciation, même face à des attaques de sérialisation ou de réflexion sophistiquées.
Bien que cette approche n’ait pas encore été adoptée à grande échelle, un type d’énumération à un seul élément est le meilleur moyen de mettre en œuvre un singleton.
Comme toutes les instances enum, Java instancie chaque objet lorsque la classe est chargée, avec certaines garanties qu'il est instancié exactement une fois par JVM . Pensez à la déclaration INSTANCE
en tant que champ final statique public: Java instanciera l'objet lors du premier appel de la classe.
Les instances sont créées lors de l’initialisation statique, définie dans le spécification de langage Java, section 12.4 .
Pour ce que cela vaut, Joshua Bloch décrit ce modèle en détail sous le point 3 de Effective Java Deuxième édition .
Puisque Singleton Pattern consiste à avoir un constructeur privé et à appeler une méthode pour contrôler les instanciations (comme certains getInstance
), dans Enums nous avons déjà un constructeur privé implicite.
Je ne sais pas exactement comment JVM ou quelque conteneur contrôle les instances de notre Enums
, mais il semble qu'il utilise déjà un implicite Singleton Pattern
, le La différence est que nous n'appelons pas un getInstance
, nous appelons simplement Enum.
Comme cela a été mentionné dans une certaine mesure auparavant, une énumération est une classe Java avec la condition particulière que sa définition doit commencer par au moins une "constante d'énumération".
En partie, c'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 selon ces lignes:
MySingleton.INSTANCE.doSomething();
String something = MySingleton.INSTANCE.getSomething();
Comme indiqué dans d’autres réponses, l’utilisation d’une énumération au lieu d’une classe est principalement une instanciation thread-safe du singleton et une garantie que ce ne sera toujours qu’une copie.
Et, peut-être plus important encore, que ce comportement est garanti par la JVM elle-même et par la spécification Java.
Voici une section de spécification Java sur la manière dont plusieurs instances d'une instance enum sont empêchées:
Un type enum n'a pas d'autres instances que celles définies par ses constantes enum. Tenter d'instancier explicitement un type enum est une erreur de compilation. La méthode de clonage finale dans Enum garantit que les constantes enum 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éflexive des types enum est interdite. Ensemble, ces quatre éléments garantissent qu'il n'existe aucune instance d'un type enum autre que celles définies par les constantes enum.
Il est à noter qu’après l’instanciation, tous les problèmes de sécurité des threads doivent être traités comme dans toute autre classe avec le mot clé synchronized, etc.