Vous avez récemment approfondi le modèle de conception singleton , comment l'implémenter en Java et à quoi il sert. Mais que se passe-t-il si je vous dis que Java est livré avec son propre singleton prêt à l'emploi ? Intrigué ? Alors plongeons dedans.

Vous connaissez probablement déjà la classe Enum . Il a une particularité dont vous devez être conscient. Plus précisément, Enum implémente le modèle de conception singleton. Cette option est presque la même que l'approche singleton impliquant un champ public .

Singleton en tant qu'énumération :


public enum Device {   
    PRINTER	
} 
    

Singleton en tant que variable publique :


public class Printer {   
    public static final Printer PRINTER = new Printer();   
    private Printer() {
    }
//…
}
    

L' approche enum est plus compacte que l'approche du champ public, car nous n'avons pas besoin d'écrire notre propre implémentation. Plus important encore, les énumérations n'ont aucun problème avec la sérialisation.

La sérialisation des énumérations fonctionne différemment de celle des objets ordinaires : seule la valeur du nom de l'énumération est sérialisée. Lors de la désérialisation, la méthode est utilisée avec le nom désérialisé pour obtenir une instance. De plus, enum peut vous protéger contre les attaques par réflexion .

Vous en apprendrez plus sur la réflexion dans les leçons du deuxième module, où nous explorerons l' API Reflection .

Java interdit l'instanciation des énumérations - une limitation intégrée à l'implémentation de la méthode newInstance de la classe Constructor , qui est souvent appelée lors de la création d'objets par réflexion.

Extrait de code de Constructor.newInstance . Utilisé pour créer un enum :


if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");
    

Les inconvénients de l'utilisation d'une énumération pour créer un singleton incluent :

  • Absence d'initialisation paresseuse, car l'objet est créé immédiatement et l'initialisation ne peut pas être retardée.

  • Les autres classes ne peuvent pas être prolongées. Autrement dit, dans les cas où vous devez hériter d'une autre classe, il ne fonctionnera pas d'utiliser une énumération comme singleton. Dans de tels cas, nous devons nous tourner vers les autres options d'implémentation qui nous sont déjà familières : une méthode statique ou une variable publique.

  • Lorsque vous utilisez enum comme singleton, vous ne pouvez utiliser qu'un seul champ enum .


public enum Device extends Electricity { 
    PRINTER 
}
    

Ce code nous donnera une erreur de compilation :

Aucune clause d'extension autorisée pour l'énumération

Mais si nous devons implémenter une interface, il n'y a pas de problème, puisque enum peut implémenter des interfaces :


public enum Device implements Electricity { 
    PRINTER 
}
    

Si vous n'avez pas besoin d'utiliser l'héritage, il est préférable d'implémenter le modèle singleton via enum . Nous ne sommes pas les seuls à le recommander - Joshua Bloch lui-même le fait aussi .

Cette approche de mise en œuvre vous offre commodité, compacité, sérialisation prête à l'emploi, protection contre les attaques par réflexion et unicité - tout ce dont un bon singleton a besoin !