web-dev-qa-db-fra.com

Énumérations Java et instructions Switch - le cas par défaut?

Pour les personnes suggérant de lancer une exception:
Lancer une exception ne me donne pas une erreur de compilation, mais une erreur d'exécution. Je sais que je peux lancer une exception, je préfère mourir pendant la compilation que pendant l'exécution.

Tout d'abord, j'utilise Eclipse 3.4.

J'ai un modèle de données qui a une propriété de mode qui est un Enum.

enum Mode {on(...), off(...), standby(...); ...}

Je suis en train d'écrire une vue de ce modèle et j'ai le code

...
switch(model.getMode()) {
case on:
   return getOnColor();
case off:
   return getOffColor();
case standby:
   return getStandbyColor();
}
...

Je reçois une erreur "Cette méthode doit renvoyer un résultat de type Java.awt.Color" car je n'ai ni casse par défaut ni retour xxx à la fin de la fonction . I want une compilation error dans le cas où quelqu'un ajoute un autre type à l'énumération (par exemple shuttingdown), donc je ne veux pas mettre un cas par défaut qui lève une AssertionError, car cela compilera avec un Mode modifié et ne sera pas vu comme une erreur jusqu'à l'exécution.

Ma question est la suivante:
Pourquoi EclipseBuilder (et javac) ne reconnaît-il pas que ce commutateur couvre toutes les possibilités (ou les couvre-t-il?) Et cesse de m'avertir de la nécessité d'un type de retour. Existe-t-il un moyen de faire ce que je veux sans ajouter de méthodes au mode?

À défaut, existe-t-il une option d'avertissement/d'erreur sur les instructions de commutateur qui ne couvrent pas toutes les valeurs possibles de Enum?

Edit: Rob: C'est une compilation erreur. Je viens d'essayer de le compiler avec javac et j'obtiens une erreur "instruction de retour manquante" ciblant le dernier} de la méthode. Eclispe place simplement l'erreur en haut de la méthode.

55
KitsuneYMG

Vous pouvez toujours utiliser le modèle Enum avec visiteur:

enum Mode {
  on {
      public <E> E accept( ModeVisitor<E> visitor ) {
         return visitor.visitOn();
      }
  },
  off {
      public <E> E accept( ModeVisitor<E> visitor ) {
         return visitor.visitOff();
      }
  },
  standby {
      public <E> E accept( ModeVisitor<E> visitor ) {
         return visitor.visitStandby();
      }
  }

  public abstract <E> E accept( ModeVisitor<E> visitor );

  public interface ModeVisitor<E> {
      E visitOn();
      E visitOff();
      E visitStandby();
  }
}

Ensuite, vous implémenterez quelque chose comme ce qui suit:

public final class ModeColorVisitor implements ModeVisitor<Color> {
    public Color visitOn() {
       return getOnColor();
    }

    public Color visitOff() {
       return getOffColor();
    }

    public Color visitStandby() {
       return getStandbyColor();
    }

}

Vous l'utiliseriez comme suit:

return model.getMode().accept( new ModeColorVisitor() );

C'est beaucoup plus détaillé, mais vous obtiendrez immédiatement une erreur de compilation si une nouvelle énumération était déclarée.

70
mtpettyp

Vous devez activer dans Eclipse (fenêtre -> Préférences) les paramètres "Constante de type Enum non couverte dans le commutateur" avec le niveau Erreur.

Lance une exception à la fin de la méthode, mais n'utilise pas la casse par défaut.

public String method(Foo foo)
  switch(foo) {
  case x: return "x";
  case y: return "y";
  }

  throw new IllegalArgumentException();
}

Maintenant, si quelqu'un ajoute un nouveau cas plus tard, Eclipse lui fera savoir qu'il manque un cas. Donc, n'utilisez jamais la valeur par défaut, à moins que vous n'ayez de très bonnes raisons de le faire.

56
egaga

Je ne sais pas pourquoi vous obtenez cette erreur, mais voici une suggestion: pourquoi ne définissez-vous pas la couleur dans l'énum elle-même? Ensuite, vous ne pouvez pas oublier accidentellement de définir une nouvelle couleur.

Par exemple:

import Java.awt.Color;

public class Test {

    enum Mode 
    {
        on (Color.BLACK), 
        off (Color.RED),
        standby (Color.GREEN);

        private final Color color; 
        Mode (Color aColor) { color = aColor; }
        Color getColor() { return color; }
    }

    class Model
    {
        private Mode mode;
        public Mode getMode () { return mode; }
    }

    private Model model;

    public Color getColor()
    {
        return model.getMode().getColor();
    }   
}

bTW, pour la comparaison est ici le cas original, avec erreur du compilateur. 

import Java.awt.Color;
public class Test {

    enum Mode {on, off, standby;}

    class Model
    {
        private Mode mode;
        public Mode getMode () { return mode; }
    }

    private Model model;

    public Color getColor()
    {
        switch(model.getMode()) {
        case on:
           return Color.BLACK;
        case off:
           return Color.RED;
        case standby:
           return Color.GREEN;
        }
    }   
}
10
amarillion

Je dirais que c'est probablement parce que model.GetMode () pourrait retourner null.

6
Paul Sonier

Votre problème est que vous essayez d'utiliser l'instruction switch comme indicateur du verrouillage de votre enum.

Le fait est que l'instruction 'switch' et le compilateur Java ne peuvent pas reconnaître que vous ne souhaitez pas autoriser d'autres options dans votre enum. Le fait que vous souhaitiez seulement trois options dans votre enum est complètement distinct de votre conception de l'instruction switch, qui, comme d'autres l'ont noté, devrait TOUJOURS avoir une instruction par défaut. (Dans votre cas, il devrait renvoyer une exception car il s'agit d'un scénario non géré.) 

Vous devriez saupoudrer généreusement votre enum de commentaires pour que tout le monde sache ne pas y toucher, et vous devriez corriger votre instruction switch afin de générer des erreurs pour les cas non reconnus . Ainsi, vous avez couvert toutes les bases.

MODIFIER

Sur la question de jeter une erreur du compilateur. Cela n’a pas vraiment de sens. Vous avez une énumération avec trois options et un commutateur avec trois options. Vous voulez qu'il génère une erreur de compilation si quelqu'un ajoute une valeur à l'énumération. Sauf que les énumérations peuvent être de n'importe quelle taille, il est donc inutile de générer une erreur de compilation si quelqu'un la modifie. De plus, vous définissez la taille de votre enum en fonction d'une instruction switch pouvant être située dans une classe complètement différente. 

Le fonctionnement interne d’Enum et de Switch est complètement séparé et doit rester non couplé. 

2
DevinB

Une bonne solution consiste à ajouter le cas par défaut pour renvoyer une valeur d'erreur ou une exception et d'utiliser des tests automatisés avec jUnit, par exemple:

@Test
public void testEnum() {
  for(Mode m : Mode.values() {
    m.foobar(); // The switch is separated to a method
    // If you want to check the return value, do it (or if there's an exception in the 
    // default part, that's enough)
  }
}

Lorsque vous avez des tests automatisés, cela va prendre en compte que foobar est défini pour toutes les énumérations.

2
Touko

Créez un cas par défaut qui lève une exception:

throw new RuntimeExeption("this code should never be hit unless someone updated the enum") 

... et cela explique assez bien pourquoi Eclipse se plaint: votre commutateur peut couvrir tous les cas enum aujourd'hui, mais quelqu'un pourrait ajouter un cas et ne pas recompiler demain.

2
kdgregory

Pourquoi EclipseBuilder ne reconnaît-il pas que ce commutateur couvre toutes les possibilités (ou les couvre-t-il?) Et cesse de m'avertir de la nécessité d'un type de retour. Existe-t-il un moyen de faire ce que je veux sans ajouter de méthodes au mode?

Ce n'est pas un problème dans Eclipse, mais plutôt le compilateur, javac. Tout ce que javac voit, c’est que vous n’avez pas de valeur de retour dans le cas où rien ne correspond (le fait que vous savez que vous correspondez à tous les cas n’est pas pertinent). Vous devez retourner quelque chose dans le cas par défaut (ou lever une exception).

Personnellement, je voudrais simplement lancer une sorte d'exception.

2
mipadi

Puisque je ne peux pas me contenter de commenter ...

  1. Toujours, toujours, toujours avoir un cas par défaut. Vous seriez surpris de voir à quelle fréquence il sera touché (moins en Java que C, mais quand même). 

  2. Cela dit, que se passe-t-il si je ne souhaite gérer que l’activation/désactivation dans mon cas? Votre traitement sémantique par javac signalerait cela comme un problème.

0
Paul

De nos jours (cette réponse est écrite plusieurs années après la question initiale), Eclipse permet de suivre la configuration dans Fenêtre -> Préférences -> Java -> Compilateur -> Erreur/avertissements -> Problèmes de programmation potentiels:

Boîtiers de commutation incomplets 

Signal même si le cas par défaut existe

0
pasaba por aqui