Pourquoi utiliser une fonctionnalité d'un langage de programmation ? Si nous avons des langages, c'est pour
- Les programmeurs doivent être en mesure d'assurer l'efficacité et l'efficience de la gestion de l'information. correctement exprimer des algorithmes sous une forme utilisable par les ordinateurs.
- Les responsables de la maintenance doivent comprendre les algorithmes que d'autres ont écrits et correctement apporter des modifications.
Les Enums améliorent à la fois la probabilité d'exactitude et la lisibilité sans avoir à écrire de nombreux documents de référence. Si vous êtes prêt à écrire du matériel de bureau, vous pouvez alors "simuler" les enums :
public class Color {
private Color() {} // Prevent others from making colors.
public static final Color RED = new Color();
public static final Color AMBER = new Color();
public static final Color GREEN = new Color();
}
Vous pouvez maintenant écrire :
Color trafficLightColor = Color.RED;
Le modèle ci-dessus a à peu près le même effet que
public enum Color { RED, AMBER, GREEN };
Les deux fournissent le même niveau d'aide à la vérification de la part du compilateur. Le modèle de base n'est rien d'autre qu'un surcroît de saisie. Mais l'économie d'une grande partie de la saisie rend le programmeur plus efficace (voir 1), il s'agit donc d'une fonctionnalité intéressante.
Cela vaut la peine pour au moins une autre raison :
Déclaration d'échange de données
Une chose que la static final
La simulation de l'enum ci-dessus fait pas vous donner est agréable switch
cas. Pour les types d'énumération, le commutateur Java utilise le type de sa variable pour déduire la portée des cas d'énumération. enum Color
ci-dessus, il suffit de dire :
Color color = ... ;
switch (color) {
case RED:
...
break;
}
Notez qu'il ne s'agit pas Color.RED
dans les affaires. Si vous n'utilisez pas d'enum, la seule façon d'utiliser des quantités nommées avec des switch
est quelque chose comme :
public Class Color {
public static final int RED = 0;
public static final int AMBER = 1;
public static final int GREEN = 2;
}
Mais maintenant, une variable qui contient une couleur doit être de type int
. L'agréable vérification par le compilateur de l'enum et de l'élément static final
La simulation a disparu. Pas de quoi se réjouir.
Un compromis consiste à utiliser un membre à valeur scalaire dans la simulation :
public class Color {
public static final int RED_TAG = 1;
public static final int AMBER_TAG = 2;
public static final int GREEN_TAG = 3;
public final int tag;
private Color(int tag) { this.tag = tag; }
public static final Color RED = new Color(RED_TAG);
public static final Color AMBER = new Color(AMBER_TAG);
public static final Color GREEN = new Color(GREEN_TAG);
}
Aujourd'hui :
Color color = ... ;
switch (color.tag) {
case Color.RED_TAG:
...
break;
}
Mais attention, encore un peu plus d'éléments de langage !
Utilisation d'un enum en tant que singleton
D'après le modèle ci-dessus, vous pouvez voir pourquoi une énumération permet d'implémenter un singleton. Au lieu d'écrire :
public class SingletonClass {
public static final void INSTANCE = new SingletonClass();
private SingletonClass() {}
// all the methods and instance data for the class here
}
et d'y accéder avec
SingletonClass.INSTANCE
nous pouvons simplement dire
public enum SingletonClass {
INSTANCE;
// all the methods and instance data for the class here
}
qui nous donne la même chose. Nous pouvons nous en sortir parce que les enums Java sont mis en œuvre comme des classes à part entière, avec seulement un peu de sucre syntaxique saupoudré sur le dessus. C'est une fois de plus un peu moins de "boilerplate", mais ce n'est pas évident à moins que l'idiome ne vous soit familier. Je n'aime pas non plus le fait que vous obteniez les diverses fonctions enum même si elles n'ont pas beaucoup de sens pour le singleton : ord
y values
etc. (Il existe en fait une simulation plus délicate dans laquelle Color extends Integer
qui fonctionnera avec switch, mais c'est tellement délicat que cela montre encore plus clairement pourquoi enum
est une meilleure idée.)
Sécurité des fils
La sécurité des threads n'est un problème potentiel que lorsque des singletons sont créés paresseusement sans verrouillage.
public class SingletonClass {
private static SingletonClass INSTANCE;
private SingletonClass() {}
public SingletonClass getInstance() {
if (INSTANCE == null) INSTANCE = new SingletonClass();
return INSTANCE;
}
// all the methods and instance data for the class here
}
Si plusieurs threads appellent getInstance
simultanément tout en INSTANCE
est toujours nul, un nombre quelconque d'instances peut être créé. Ce n'est pas une bonne chose. La seule solution est d'ajouter synchronized
l'accès pour protéger la variable INSTANCE
.
Toutefois, le static final
ci-dessus ne présente pas ce problème. Il crée l'instance avec empressement au moment du chargement de la classe. Le chargement de la classe est synchronisé.
En enum
est effectivement paresseux car il n'est pas initialisé avant la première utilisation. L'initialisation Java est également synchronisée, de sorte que plusieurs threads ne peuvent pas initialiser plus d'une instance de INSTANCE
. Vous obtenez un singleton initialisé paresseusement avec très peu de code. Le seul point négatif est la syntaxe plutôt obscure. Vous devez connaître l'idiome ou comprendre parfaitement le fonctionnement du chargement et de l'initialisation des classes pour savoir ce qui se passe.
9 votes
Dans son livre Java efficace, deuxième édition Joshua Bloch développe cette approche dans Point 3 : Renforcer la propriété Singleton avec un constructeur privé ou un type enum réimprimé dans Dr. Dobb's .
1 votes
Vous ne pouvez pas instancier les enums parce qu'ils ont un constructeur privé, ils sont instanciés au démarrage de jvm, donc c'est un singleton, les enums ne sont pas à l'abri des threads.