Bisher haben wir in die letzte Zeile eine break-Anweisung gesetzt. Ohne ein break würden nach einer Übereinstimmung alle nachfolgenden Anweisungen ausgeführt. Sie laufen somit in einen neuen Abschnitt herein, bis ein break oder das Ende von switch erreicht ist. Da dies vergleichbar mit einem Spielzeug ist, bei dem Kugeln von oben nach unten durchfallen, nennt sich dieses auch Fall-through. Ein häufiger Programmierfehler ist, das break zu vergessen, und daher sollte ein beabsichtigter Fall-through immer als Kommentar angegeben werden.
Über dieses Durchfallen ist es möglich, bei unterschiedlichen Werten immer die gleiche Anweisung ausführen zu lassen. Das nutzt auch das nächste Beispiel, das einen kleinen Parser für einfache Datumswerte realisiert. Der Parser soll mit drei unterschiedlichen Datumsangeben umgehen können, je ein Beispiel:
- „18 12“: Jahr in Kurzform, Monat
- „2018 12“: Jahr, Monat
- „12“: Nur Monat, es soll das aktuelle Jahr implizit gelten
public class SimpleYearMonthParser { @SuppressWarnings( "resource" ) public static void main( String[] args ) { String date = "17 12"; int month = 0, year = 0; java.util.Scanner scanner = new java.util.Scanner( date ); switch ( date.length() ) { case 5: // YY MM year = 2000; // Fall-through case 7: // YYYY MM year += scanner.nextInt(); // Fall-through case 2: // MM month = scanner.nextInt(); if ( year == 0 ) year = java.time.Year.now().getValue(); break; default : System.err.println( "Falsches Format" ); } System.out.println( "Monat=" + month + ", Jahr=" + year ); } }
In dem Beispiel bestimmt eine case-Anweisung über die Länge, wie der Aufbau ist. Ist die Länge 5, so ist die Angabe des Jahres verkürzt und wir initialisieren das Jahr mit 2000, um im folgenden Schritt mit Hilfe vom Scanner das Jahr einzulesen. Zu diesem Schritt wären wir auch direkt gekommen, wenn die Länge der Eingabe 7 gewesen wäre, also das Jahr vierstellig gewesen wäre. Damit ist der Jahresanteil geklärt, es bleibt die Monate zu parsen. Kommen wir direkt über einen String der Länge 2, ist vorher kein Jahr gesetzt, wir bekommen über java.time.Year.now().getValue() das aktuelle Jahr, andernfalls überschreiben wir die Variable nicht.
Was sollte der Leser von diesem Beispiel mitnehmen? Eigentlich nur Kopfschütteln für eine schwer zu verstehende Lösung. Das Durchfallen ist eigentlich nur zur Zusammenfassung mehrerer case-Blöcke sinnvoll zu verwenden.
Sprachvergleich *
Obwohl ein fehlendes break zu lästigen Programmierfehlern führt, haben die Entwickler von Java dieses Verhalten vom syntaktischen Vorgänger C übernommen. Eine interessante andere Lösung wäre gewesen, das Verhalten genau umzudrehen und das Durchfallen explizit einzufordern, zum Beispiel mit einem Schlüsselwort. Dazu gibt es eine interessante Entwicklung: Java »erbt« diese Eigenschaft von C(++), die wiederum erbt sie von der Programmiersprache B. Einer der »Erfinder« von B ist Ken Thompson, der heute bei Google arbeitet und an der neuen Programmiersprache Go beteiligt ist. In Go müssen Entwickler ausdrücklich die fallthrough-Anweisung verwenden, wenn ein case-Block zum nächsten weiterleiten soll. Das gleiche gilt für die neue Programmiersprache Swift; auch hier gibt es die Anweisung fallthrough. Selbst in C++ gibt es seit dem Standard C++17 das Standard-Attribut [[fallthrough]][1] – Attribute sind mit Java-Annotationen vergleichbar ist; es steuert den Compiler, bei einem Durchfallen keine Warnung anzuzeigen.
Stack-Case-Labels
Stehen mehrere case-Blöcke untereinander, um damit Bereiche abzubilden, nennt sich das auch Stack-Case-Labels. Nehmen wir an, eine Variable hour steht für eine Stunde am Tag und wir wollen herausfinden, ob Mittagsruhe, Nachtruhe oder Arbeitszeit ist:
int hour = 12; switch ( hour ) { // Nachtruhe von 22 Uhr bis 6 Uhr case 22: case 23: case 24: case 0: case 1: case 2: case 3: case 4: case 5: System.out.println( "Nachtruhe" ); break; // Mittagsruhe von 13 bis 15 Uhr case 13: case 14: System.out.println( "Mittagsruhe" ); break; default : System.out.println( "Arbeiten" ); break; }
[1] http://en.cppreference.com/w/cpp/language/attributes