wake-up-neo.net

Warum brauchen wir Break-after-Case-Statements?

Warum fügt der Compiler nach jedem Codeblock nicht automatisch break -Anweisungen in den Switch ein? Ist es aus historischen Gründen? Wann sollen mehrere Codeblöcke ausgeführt werden?

85
unj2

Manchmal ist es hilfreich, mehrere Fälle mit demselben Codeblock zu verknüpfen, z. B.

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

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

usw. Nur ein Beispiel.

Nach meiner Erfahrung ist es in der Regel ein schlechter Stil, "durchzufallen" und mehrere Codeblöcke für einen Fall ausführen zu lassen. In einigen Situationen kann es jedoch zu Verwendungen kommen.

90
WildCrustacean

Historisch , weil die case im Wesentlichen eine label definiert hat, die auch als Zielpunkt eines goto-Aufrufs bezeichnet wird. Die switch-Anweisung und die zugehörigen Fälle repräsentieren tatsächlich nur einen Mehrfachzweig mit mehreren potenziellen Eintrittspunkten in einen Codestrom.

Alles in allem wurde festgestellt, dass break fast immer das Standardverhalten ist, das Sie am Ende eines jeden Falles bevorzugen.

29
Bob Cross

Java kommt von C und das ist die Syntax von C.

Es gibt Zeiten, in denen mehrere Case-Anweisungen nur einen Ausführungspfad haben sollen .. __ Nachfolgend ein Beispiel, in dem angegeben wird, wie viele Tage im Monat.

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

Sie können mit Case Case Through alle möglichen interessanten Dinge erledigen.

Nehmen wir zum Beispiel an, Sie möchten eine bestimmte Aktion für alle Fälle ausführen, aber in einem bestimmten Fall möchten Sie diese Aktion plus etwas anderes ausführen. Die Verwendung einer switch-Anweisung mit Fall-Through würde es sehr einfach machen.

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

Natürlich ist es leicht, die break-Anweisung am Ende eines Falls zu vergessen und unerwartetes Verhalten zu verursachen. Gute Compiler werden Sie warnen, wenn Sie die break-Anweisung weglassen. 

13
Zach Johnson

Ich denke es ist ein Fehler. Als Sprachkonstrukt ist es genauso einfach, break als Standard zu verwenden und stattdessen ein fallthrough-Schlüsselwort zu verwenden. Der meiste Code, den ich geschrieben und gelesen habe, hat nach jedem Fall eine Pause.

13
James K Polk

Warum setzt der Compiler die break-Anweisungen nicht automatisch nach jedem Codeblock in den Switch?

Abgesehen von dem guten Wunsch, in der Lage zu sein, den identischen Block für mehrere Fälle (die speziell behandelt sein könnten) zu verwenden ...

Ist es aus historischen Gründen? Wann möchten Sie mehrere Codeblöcke ausführen?

Es dient hauptsächlich der Kompatibilität mit C und ist vermutlich ein uralter Hack aus früheren Zeiten, als goto Keywords die Erde durchstreiften. Es ermöglicht natürlich einige erstaunliche Dinge, wie Duffs Gerät , aber ob das ein Vorteil oder ein Nachteil ist ist… bestenfalls argumentativ.

7
Donal Fellows

Sie müssen also keinen Code wiederholen, wenn Sie mehrere Fälle benötigen, um dasselbe zu tun:

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

Oder Sie können Dinge tun wie:

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

Kaskadenweise.

Wirklich Fehler/Verwirrung, wenn Sie mich fragen.

4
Francisco Soto

Java wird von C abgeleitet, dessen Erbe eine als Duff's Device ..__ bekannte Technik beinhaltet. Diese Optimierung beruht auf der Tatsache, dass die Kontrolle von Fall zu Fall durchgeht, wenn keine break;-Anweisung vorliegt. Zu der Zeit, als C standardisiert wurde, gab es eine Menge Code wie "in der Wildnis", und es wäre kontraproduktiv gewesen, die Sprache zu ändern, um solche Konstruktionen zu brechen. 

3
Jim Lewis

Nach den historischen Aufzeichnungen erfand Tony Hoare in den 1960er Jahren während der Revolution des "strukturierten Programmierens" die Fallaussage. Tonys Case-Anweisung unterstützte mehrere Labels pro Case und automatisches Beenden ohne stinkende break-Anweisungen. Die Forderung nach einer expliziten break war etwas, was aus der BCPL/B/C-Linie kam. Dennis Ritchie schreibt (in ACM HOPL-II):

Beispielsweise war der Endfall, der einer BCPL-Switchon-Anweisung entgeht, in der Sprache .__ nicht vorhanden. als wir es in den 1960ern lernten, und damit die Überladung des break-Schlüsselwortes zu entkommen Die Aussage von B und C wechselt eher von einer abweichenden Entwicklung als von einer bewussten Veränderung.

Ich habe keine historischen Schriften über BCPL finden können, aber Ritchies Kommentar lässt vermuten, dass die break mehr oder weniger ein historischer Zufall war. BCPL hat das Problem später behoben, aber vielleicht waren Ritchie und Thompson zu sehr damit beschäftigt, Unix zu erfinden, um sich mit so einem Detail zu beschäftigen :-)

3
Norman Ramsey

Die Variable break after switch cases wird verwendet, um den Durchbruch in den Anweisungen von switch zu vermeiden. Interessanterweise kann dies jetzt durch die neu gebildeten Schalteretiketten erreicht werden, die über JEP-325 implementiert wurden. 

Mit diesen Änderungen kann die break mit jedem Schalter case vermieden werden, wie weiter gezeigt wird: -

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");
        }
    }
}

Bei Ausführen des obigen Codes mit JDK-12 könnte der vergleichende Ausgang als gesehen werden 

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

und 

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

und natürlich die Sache unverändert

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

Wie die Leute vorher sagten, ist es, einen Durchbruch zuzulassen, und es ist kein Fehler, es ist eine Funktion. Wenn Sie zu viele break-Anweisungen stören, können Sie sie einfach mit return-Anweisungen entfernen. Dies ist in der Tat eine gute Praxis, da Ihre Methoden so klein wie möglich sein sollten (aus Gründen der Lesbarkeit und Wartbarkeit). Daher ist eine switch-Anweisung bereits groß genug für eine Methode. Daher sollte eine gute Methode nichts anderes enthalten ist ein Beispiel:

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");
        }        
    }
}   

Die Ausführung druckt:

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

wie erwartet.

1
Víctor Gil

Wenn der Compiler keine automatische Unterbrechung hinzugefügt hat, ist es möglich, einen Schalter/Fall zu verwenden, um auf Bedingungen wie 1 <= a <= 3 zu testen, indem die break-Anweisung aus 1 und 2 entfernt wird.

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

weil es Situationen gibt, in denen Sie den ersten Block durchlaufen möchten, um beispielsweise zu vermeiden, dass derselbe Code in mehreren Blöcken geschrieben wird, aber dennoch in der Lage sind, sie für die Mroe-Steuerung zu unterteilen. Es gibt auch eine Menge anderer Gründe.

0
Jonas B

Sie können leicht andere Arten von Nummer, Monat, Anzahl zu trennen.
Dies ist besser als in diesem Fall;

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

Ich arbeite gerade an einem Projekt, bei dem ich break in meiner switch-Anweisung brauche, sonst funktioniert der Code nicht. Halten Sie sich an mich und ich werde Ihnen ein gutes Beispiel geben, warum Sie in Ihrer switch-Anweisung break benötigen. 

Stellen Sie sich vor, Sie haben drei Zustände, einen, der darauf wartet, dass der Benutzer eine Zahl eingibt, der zweite, um sie zu berechnen, und der dritte, um die Summe zu drucken. 

In diesem Fall haben Sie: 

  1. State1 - Warten Sie, bis der Benutzer eine Nummer eingegeben hat
  2. State2 - Drucken Sie die Summe
  3. state3 - Berechne die Summe

Wenn Sie sich die Zustände anschauen, möchten Sie, dass die Reihenfolge der Exaktion auf state1 , dann state3 und schließlich state2 beginnt. Andernfalls drucken wir nur Benutzereingaben, ohne die Summe zu berechnen. Um es noch einmal zu klären, warten wir, bis der Benutzer einen Wert eingibt, dann die Summe berechnen und die Summe ausgeben. 

Hier ist ein Beispielcode:

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;
    }
}

Wenn wir break nicht verwenden, wird es in dieser Reihenfolge ausgeführt: state1 , state2 und state3 . Mit break vermeiden wir dieses Szenario und können in der richtigen Prozedur anordnen, die mit state1, dann state3 und nicht zuletzt state2 beginnen soll.

0
Dler Hasan

Es ist eine alte Frage, aber eigentlich habe ich heute den Fall ohne Anweisung von break verwendet. Das Verwenden der Unterbrechung ist nicht hilfreich, wenn Sie verschiedene Funktionen nacheinander kombinieren müssen.

z.B. Verwenden von http-Antwortcodes zum Authentifizieren des Benutzers mit dem Zeittoken

serverantwortcode 401 - Token ist veraltet -> Token neu generieren und Benutzer anmelden.
Server-Antwortcode 200 - Token ist OK -> Benutzer anmelden.

im falle aussagen:

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;
        }

Wenn Sie dies verwenden, müssen Sie die Protokollfunktion für die Benutzeraktion 401 nicht aufrufen, da beim erneuten Generieren des Token die Laufzeit in den Fall 200 springt.

0