Passer au contenu principal

On a tendance à dire que les enums de Java sont équivalents à ceux en C. Erreur ! En fait ils sont bien plus puissants, et nous allons le voir à travers un exemple.

Les enums Java héritent en fait tous de la classe java.lang.Enum. Ce sont donc bien des objets, en fait les deux choses qui changent par rapport à des objets standard Java sont le mode d’instanciation et le fait que la JVM appliquera des optimisations spécifiques aux enums.

Maintenant considérons qu’on a un Enum avec trois valeurs, A, B, C. Par ailleurs on a un objet MyObject dont l’interface contient les méthodes suivantes :

   public String getForA();
   public String getForB();
   public String getForC();

La méthode classique de coder serait la suivante :

   String value = null;

   switch (myEnum) {
      A: value = myObject.getForA();
         break;
      B: value = myObject.getForB();
         break;
      C: value = myObject.getForC();
         break;
   }

Ce code présente en fait les problèmes suivants :

Il risque d’être copié/collé dans de nombreux endroits de l’application.
– Mais surtout si on ajoute une nouvelle valeur à l’enum il faudra penser à aller modifier tous les switch/case y faisant référence pour rajouter la nouvelle valeur, bref galère…
Et pourtant il existe bien plus élégant. Rappelez-vous : les enums sont des objets en Java. La solution est d’utiliser cette propriété, voici comment :

   public enum myEnum {
      A {
         @Override
         public String getValue(MyObject obj) {
            return obj.getForA();
         }
      },
      B {
         @Override
         public String getValue(MyObject obj) {
            return obj.getForB();
         }
      },
      C {
         @Override
         public String getValue(MyObject obj) {
            return obj.getForA();
         }
      };

      public abstract String getValue(MyObject obj);
   }

Dès lors en utilisant ce nouvel enum on peut réécrire le switch/case de tout à l’heure de la manière suivante :

   String value = myEnum.getValue(myObject);

Le code ci-dessus résout les problèmes énumérés, à savoir :

– Aucun risque de dupliquer le code du switch/case, étant donné que c’est maintenant la responsabilité de l’enum de savoir comment l’instance de MyObject va être invoquée suivant la valeur de l’enum.
– Si on ajoute une valeur de l’enum on pensera immédiatement à implémenter la méthode correspondante à cette valeur, sinon le code ne compilera pas.
De la même manière les enums peuvent être porteurs de valeurs si besoin.

Cet article vous a plu ? Vous aimerez sûrement aussi :

Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.

Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.

Son Twitter Son LinkedIn

Rejoignez la discussion 3 Commentaires

  • Cedric Longo dit :

    Je suis pas très fan de cette astuce de code (ou du moins de l’exemple) 😡

    En réalité, l’argument du code copié/collé me semble douteux, pour l’éviter il suffit de factoriser le bout de code avec le switch à un seul endroit. La solution c’est la factorisation, pas l’enum en soit.

    Par ailleurs, on a bien souvent tendance, voire on est obligé, d’utiliser les énums dans le code en y faisant référence explicitement.

    Et c’est là où mettre de l’implémentation de code métier dedans me pose problème, notamment si on souhaite avoir une implémentation d’environnement de test différente ou pour faire de l’injection de dépendance. Dans ces cas là, ça devient la merdouille parce qu’on fait référence à l’enum directement partout dans le code, et qu’il contient une implémentation qu’on souhaite variable.

    Dans la très grande majorité des cas, je préfère utiliser les énums que pour une liste de truc (status, phases, …), notamment en remplacement à des ids incompréhensible, tout en évitant d’y mettre à l’intérieur du code. Le code (si nécessaire) sera plutôt externalisé dans une classe métier dédiée (par exemple une factory ou un provider dans le cas cité en exemple).

    Just my 2 cents 😉

  • gojul dit :

    Bonjour,

    Alors pour vous répondre on peut tout à fait passer des enums en paramètre des méthodes, et là l’astuce évoquée prend tout son sens. Et pour être honnête ça m’est arrivé plus d’une fois.

    Ou alors avec un truc comme ça :
    Enum value = Enum.valueOf(xxx);
    value.

    Et là c’est bien plus intéressant d’utiliser le polymorphisme au niveau de l’enum que dans un switch/case.

    Le problème de la factorisation du switch case comme évoqué est en terme de maintenance, car vous risquez à un moment ou l’autre d’oublier ce bout de code le jour où vous rajoutez une valeur à l’enum. Et ce sera bien pire le jour où vous aurez plusieurs codes à modifier suivant la méthode.

    Enfin l’intérêt d’utiliser le polymorphisme pour l’enum est qu’on peut tout à fait le mocker par la suite s’il est passé en paramètre de méthode.

    Après comme toute astuce de code elle s’applique à certains cas mais pas à tous, un peu comme les design patterns quoi. 😉

  • Daniel dit :

    Pour ne pas oublier de compléter un bout de code quand on met une nouvelle valeur à l’enum, il suffit de mettre un test unitaire basique qui compte le nombre d’élément dans l’enum et le nombre de valeur différente retourné par une fonction (ou autre mais l’idée c’est le test u).

    Perso, plutôt qu’un switch (surtout pour retourner un string), je mets tout dans un dictionnaire. C’est performant et pour le test unitaire, il suffit de compter combien d’entrée a mon dictionnaire. Un oubli et le test passe rouge.

Laisser un commentaire