web-dev-qa-db-fra.com

Pourquoi avons-nous besoin de déclarations après le casse?

Pourquoi le compilateur ne met-il pas automatiquement les instructions break après chaque bloc de code dans le commutateur? Est-ce pour des raisons historiques? Quand voudriez-vous que plusieurs blocs de code soient exécutés?

85
unj2

Il est parfois utile d’avoir plusieurs cas associés au même bloc de code, tels que

case 'A':
case 'B':
case 'C':
    doSomething();
    break;

case 'D':
case 'E':
    doSomethingElse();
    break;

etc. Juste un exemple.

D'après mon expérience, il est généralement mauvais de "tomber à travers" et d'avoir plusieurs blocs de code exécuté pour un cas, mais il peut y avoir des utilisations dans certaines situations.

90
WildCrustacean

Historically , c'est parce que la case définissait essentiellement une label, également appelée point target d'un appel goto. L'instruction switch et ses cas associés ne représentent en réalité qu'une branche à plusieurs entrées avec plusieurs points d'entrée potentiels dans un flux de code.

Cela dit, il a été noté un nombre presque infini de fois que break est presque toujours le comportement par défaut que vous préférez avoir à la fin de chaque cas.

29
Bob Cross

Java vient de C et c'est la syntaxe de C.

Il arrive que vous souhaitiez que plusieurs instructions de cas aient un seul chemin d’exécution.

class SwitchDemo2 {
    public static void main(String[] args) {

        int month = 2;
        int year = 2000;
        int numDays = 0;

        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                numDays = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                numDays = 30;
                break;
            case 2:
                if ( ((year % 4 == 0) && !(year % 100 == 0))
                     || (year % 400 == 0) )
                    numDays = 29;
                else
                    numDays = 28;
                break;
            default:
                System.out.println("Invalid month.");
                break;
        }
        System.out.println("Number of Days = " + numDays);
    }
}
27
Romain Hippeau

Vous pouvez faire toutes sortes de choses intéressantes en cas de chute de dossier.

Par exemple, supposons que vous souhaitiez effectuer une action particulière pour tous les cas, mais que dans certains cas, vous souhaitiez effectuer cette action plus autre chose. Utiliser une instruction switch avec fall-through rendrait cela très facile.

switch (someValue)
{
    case extendedActionValue:
        // do extended action here, falls through to normal action
    case normalActionValue:
    case otherNormalActionValue:
        // do normal action here
        break;
}

Bien sûr, il est facile d’oublier l’instruction break à la fin d’un cas et de provoquer un comportement inattendu. Les bons compilateurs vous préviendront lorsque vous omettez l’instruction break. 

13
Zach Johnson

Je pense que c'est une erreur. En tant que construction de langage, il est tout aussi facile d’avoir break comme valeur par défaut et d’avoir à la place un mot clé fallthrough. La plupart du code que j'ai écrit et lu a une pause après chaque cas.

13
James K Polk

Pourquoi le compilateur ne met-il pas automatiquement les instructions break après chaque bloc de code dans le commutateur?

Laissant de côté le bon désir de pouvoir utiliser le même bloc pour plusieurs cas (ce qui pourrait être un cas spécial) ...

Est-ce pour des raisons historiques? Quand voudriez-vous que plusieurs blocs de code soient exécutés?

C’est principalement pour la compatibilité avec C, et c’est un hack très ancien du temps où goto mots-clés erraient sur la Terre. Il fait permet des choses étonnantes, bien sûr, telles que Le périphérique de Duff , mais que ce soit en faveur ou contre est… argumentatif au mieux.

6
Donal Fellows

Donc, vous n'avez pas à répéter le code si vous avez besoin de plusieurs cas pour faire la même chose:

case THIS:
case THAT:
{
    code;
    break;
}

Ou vous pouvez faire des choses comme:

case THIS:
{
   do this;
}
case THAT:
{
   do that;
}

En cascade.

Vraiment bug/confusion sujette, si vous me demandez.

4
Francisco Soto

Java est dérivé de C, dont l'héritage inclut une technique connue sous le nom de Le périphérique de Duff . C'est une optimisation qui repose sur le fait que le contrôle passe d'un cas à l'autre, en l'absence d'un énoncé break;. À l'époque de la normalisation de C, il y avait beaucoup de code comme celui-ci "à l'état sauvage", et il aurait été contre-productif de changer le langage pour casser de telles constructions. 

3
Jim Lewis

En ce qui concerne l'historique, Tony Hoare a inventé la déclaration de cas dans les années 1960, pendant la révolution de la "programmation structurée". La déclaration de cas de Tony prenait en charge plusieurs libellés par cas et sortie automatique sans instruction break puante. L'exigence d'une break explicite est venue de la ligne BCPL/B/C. Dennis Ritchie écrit (dans ACM HOPL-II):

Par exemple, le cas qui s'échappe d'une instruction d'activation BCPL n'était pas présent dans la langue quand nous l’avons appris dans les années 1960, et donc la surcharge du mot-clé break pour nous échapper. de la déclaration de commutateur B et C doit à l'évolution divergente plutôt que le changement conscient.

Je n'ai pas trouvé d'écrits historiques sur BCPL, mais le commentaire de Ritchie suggère que le break était plus ou moins un accident historique. BCPL a plus tard résolu le problème, mais peut-être que Ritchie et Thompson étaient trop occupés à inventer Unix pour être dérangés par un tel détail :-)

3
Norman Ramsey

La break après le commutateur cases est utilisée pour éviter la chute dans les instructions de commutateur. Bien que cela soit intéressant, cela peut maintenant être réalisé grâce aux étiquettes de commutateur nouvellement formées, implémentées via JEP-325

Avec ces modifications, la break avec chaque commutateur case peut être évitée, comme illustré plus loin: -

public class SwitchExpressionsNoFallThrough {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int value = scanner.nextInt();
        /*
         * Before JEP-325
         */
        switch (value) {
            case 1:
                System.out.println("one");
            case 2:
                System.out.println("two");
            default:
                System.out.println("many");
        }

        /*
         * After JEP-325
         */
        switch (value) {
            case 1 ->System.out.println("one");
            case 2 ->System.out.println("two");
            default ->System.out.println("many");
        }
    }
}

Sur en exécutant le code ci-dessus avec JDK-12 , la sortie comparative peut être vue comme 

//input
1
// output from the implementation before JEP-325
one
two
many
// output from the implementation after JEP-325
one

et 

//input
2
// output from the implementation before JEP-325
two
many
// output from the implementation after JEP-325
two

et bien sûr la chose inchangée

// input
3
many // default case match
many // branches to 'default' as well
2
nullpointer

Comme les gens l'ont déjà dit, il s'agit de laisser tomber et ce n'est pas une erreur, c'est une fonctionnalité. Si trop d'instructions break vous agacent, vous pouvez facilement vous en débarrasser en utilisant plutôt des instructions return. C’est en fait une bonne pratique, car vos méthodes doivent être aussi petites que possible (pour des raisons de lisibilité et de maintenabilité), de sorte qu’une instruction switch est déjà assez grande pour une méthode et qu’une bonne méthode ne doit donc rien contenir d’autre. est un exemple:

public class SwitchTester{
    private static final Log log = LogFactory.getLog(SwitchTester.class);
    public static void main(String[] args){
        log.info(monthsOfTheSeason(Season.WINTER));
        log.info(monthsOfTheSeason(Season.SPRING));
        log.info(monthsOfTheSeason(Season.SUMMER));
        log.info(monthsOfTheSeason(Season.AUTUMN));
    }

    enum Season{WINTER, SPRING, SUMMER, AUTUMN};

    static String monthsOfTheSeason(Season season){
        switch(season){
            case WINTER:
                return "Dec, Jan, Feb";
            case SPRING:
                return "Mar, Apr, May";
            case SUMMER:
                return "Jun, Jul, Aug";
            case AUTUMN:
                return "Sep, Oct, Nov";
            default:
                //actually a NullPointerException will be thrown before reaching this
                throw new IllegalArgumentException("Season must not be null");
        }        
    }
}   

L'exécution imprime:

12:37:25.760 [main] INFO lang.SwitchTester - Dec, Jan, Feb
12:37:25.762 [main] INFO lang.SwitchTester - Mar, Apr, May
12:37:25.762 [main] INFO lang.SwitchTester - Jun, Jul, Aug
12:37:25.762 [main] INFO lang.SwitchTester - Sep, Oct, Nov

comme prévu.

1
Víctor Gil

Ne pas avoir de rupture automatique ajoutée par le compilateur permet d'utiliser un commutateur/cas pour tester des conditions telles que 1 <= a <= 3 en supprimant l'instruction break de 1 et 2.

switch(a) {
  case 1: //I'm between 1 and 3
  case 2: //I'm between 1 and 3
  case 3: //I'm between 1 and 3
          break;
}
0
Soufiane Hassou

par exemple, pour éviter d'écrire le même code dans plusieurs blocs tout en conservant la possibilité de les diviser pour un contrôle plus précis, il est parfois nécessaire de parcourir le premier bloc. Il y a aussi une tonne d'autres raisons.

0
Jonas B

Vous pouvez faire facilement pour séparer un autre type de nombre, mois, compte.
.__ C'est mieux que si dans ce cas;

public static void spanishNumbers(String span){

    span = span.toLowerCase().replace(" ", "");
    switch (span){
     case "1":    
     case "jan":  System.out.println("uno"); break;    
     case "2":      
     case "feb":  System.out.println("dos"); break;    
     case "3":     
     case "mar":  System.out.println("tres"); break;   
     case "4":   
     case "apr":  System.out.println("cuatro"); break;
     case "5":    
     case "may":  System.out.println("cinco"); break;
     case "6":     
     case "jun":  System.out.println("seis"); break;
     case "7":    
     case "jul":  System.out.println("seite"); break;
     case "8":    
     case "aug":  System.out.println("ocho"); break;
     case "9":   
     case "sep":  System.out.println("nueve"); break;
     case "10":    
     case "oct": System.out.println("diez"); break;
     }
 }
0
John Dev

Je travaille maintenant sur un projet pour lequel j'ai besoin de break dans mon instruction switch, sinon le code ne fonctionnera pas. Restez avec moi et je vais vous donner un bon exemple de la raison pour laquelle vous avez besoin de break dans votre déclaration de commutateur. 

Imaginez que vous avez trois états, l’un qui attend que l’utilisateur entre un nombre, le second pour le calculer et le troisième pour imprimer la somme. 

Dans ce cas, vous avez: 

  1. State1 - Attend que l'utilisateur entre un numéro
  2. State2 - Imprimer la somme
  3. state3 - Calculer la somme

En regardant les états, vous voudriez que l'ordre d'exaction commence sur state1 , puis state3 et enfin state2 . Sinon, nous imprimerons uniquement les données saisies par les utilisateurs sans calculer la somme. Juste pour clarifier encore une fois, nous attendons que l'utilisateur entre une valeur, puis calculons la somme et imprimons la somme. 

Voici un exemple de code:

while(1){
    switch(state){
      case state1:
        // Wait for user input code
        state = state3; // Jump to state3
        break;
      case state2:
        //Print the sum code
        state = state3; // Jump to state3;
      case state3:
        // Calculate the sum code
        state = wait; // Jump to state1
        break;
    }
}

Si nous n'utilisons pas break, il exécutera dans cet ordre, state1 , state2 et state3 . Mais en utilisant break, nous évitons ce scénario et pouvons ordonner la bonne procédure, qui doit commencer par état1, puis état3 et enfin et surtout état2.

0
Dler Hasan

C'est une vieille question, mais en fait, je me suis heurté à l'utilisation de l'affaire sans déclaration de rupture aujourd'hui. Ne pas utiliser de pause est en réalité très utile lorsque vous devez combiner différentes fonctions en séquence.

par exemple. utilisation de codes de réponse http pour authentifier l'utilisateur avec un jeton de temps

code de réponse du serveur 401 - le jeton est obsolète -> régénérer le jeton et connectez-vous à l'utilisateur.
code de réponse du serveur 200 - le jeton est OK -> connecter l’utilisateur dans.

dans les déclarations de cas:

case 404:
case 500:
        {
            Log.v("Server responses","Unable to respond due to server error");
            break;
        }
        case 401:
        {
             //regenerate token
        }
        case 200:
        {
            // log in user
            break;
        }

Avec cette opération, il n’est pas nécessaire d’appeler le journal dans la fonction utilisateur pour la réponse 401 car lors de la régénération du jeton, le moteur d’exécution passe au cas 200.

0
Vladimír Gašpar