Dlaczego kompilator nie umieszcza automatycznie instrukcji break po każdym bloku kodu w przełączniku? Czy to z powodów historycznych? Kiedy chciałbyś wykonać wiele bloków kodu?
94
Dlaczego kompilator nie umieszcza automatycznie instrukcji break po każdym bloku kodu w przełączniku? Czy to z powodów historycznych? Kiedy chciałbyś wykonać wiele bloków kodu?
break
.Odpowiedzi:
Czasami pomocne jest powiązanie wielu przypadków z tym samym blokiem kodu, na przykład
case 'A': case 'B': case 'C': doSomething(); break; case 'D': case 'E': doSomethingElse(); break;
itd. Tylko przykład.
Z mojego doświadczenia wynika, że zazwyczaj „upadek” i wykonywanie wielu bloków kodu w jednym przypadku jest złym stylem, ale w niektórych sytuacjach może się to przydać.
źródło
// Intentional fallthrough.
gdy pominiesz przerwę. Moim zdaniem to nie tyle zły styl, ile „łatwo przez przypadek zapomnieć o przerwie”. PS Oczywiście nie w prostych przypadkach, jak w samej odpowiedzi.case
s jest ułożonych w ten sposób. Jeśli między nimi jest kod, to tak, komentarz jest prawdopodobnie zasłużony.case
, tak jak to:case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();
bez potrzeby przerwy między nimi. Pascal mógłby to zrobić: „Instrukcja case porównuje wartość wyrażenia porządkowego do każdego selektora, który może być stałą, podzakresem lub listą oddzielonych przecinkami”. ( wiki.freepascal.org/Case )Historycznie , to dlatego, że
case
był zasadniczo wyznaczającąlabel
, znany również jako punktu docelowego zgoto
rozmowy. Instrukcja switch i związane z nią przypadki tak naprawdę reprezentują gałąź wielościeżkową z wieloma potencjalnymi punktami wejścia do strumienia kodu.Wszystko to powiedziawszy, zauważono prawie nieskończoną liczbę razy, że
break
prawie zawsze jest to domyślne zachowanie, które wolałbyś mieć na końcu każdej sprawy.źródło
Java pochodzi z C i taka jest składnia z C.
Czasami chcesz, aby wiele instrukcji case miało tylko jedną ścieżkę wykonywania. Poniżej znajduje się próbka, która powie Ci, ile dni w miesiącu.
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); } }
źródło
Myślę, że to pomyłka. Jako konstrukcję językową można ją równie łatwo mieć
break
jako domyślną i zamiast tego miećfallthrough
słowo kluczowe. Większość kodu, który napisałem i przeczytałem, ma przerwę po każdej sprawie.źródło
continue <case name>
co pozwala jednoznacznie określić, z którą instrukcją przypadku kontynuować;case
w ramach prąduswitch
, po prostu staje sięgoto
. ;-)Możesz robić wiele interesujących rzeczy dzięki upadkowi obudowy.
Na przykład, powiedzmy, że chcesz wykonać określoną akcję dla wszystkich przypadków, ale w pewnym przypadku chcesz wykonać tę akcję plus coś innego. Użycie instrukcji switch z fall-through uczyniłoby to całkiem prostym.
switch (someValue) { case extendedActionValue: // do extended action here, falls through to normal action case normalActionValue: case otherNormalActionValue: // do normal action here break; }
Oczywiście łatwo jest zapomnieć o
break
stwierdzeniu na końcu sprawy i spowodować nieoczekiwane zachowanie. Dobre kompilatory ostrzeżą Cię, gdy pominiesz instrukcję break.źródło
Pomijając dobre pragnienie, aby móc używać identycznego bloku w kilku przypadkach (które mogą być w specjalnych przypadkach) ...
Dotyczy to głównie zgodności z C i prawdopodobnie jest to starożytny hack z czasów, gdy
goto
słowa kluczowe wędrowały po ziemi. To ma umożliwić niesamowite rzeczy, oczywiście, takie jak mechanizm duffa , ale czy to jest punkt na jego korzyść lub przeciwko jest ... kłótliwy w najlepsze.źródło
break
Po włączeniucase
s służy do uniknięcia fallthrough w sprawozdaniu przełącznika. Co ciekawe, można to teraz osiągnąć dzięki nowo utworzonym etykietom przełączników zaimplementowanym w JEP-325 .Dzięki tym zmianom można uniknąć
break
każdego przełącznika,case
jak pokazano dalej: -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"); } } }
Po wykonaniu powyższego kodu za pomocą JDK-12 , porównawcze dane wyjściowe mogą być postrzegane jako
//input 1 // output from the implementation before JEP-325 one two many // output from the implementation after JEP-325 one
i
//input 2 // output from the implementation before JEP-325 two many // output from the implementation after JEP-325 two
i oczywiście rzecz bez zmian
// input 3 many // default case match many // branches to 'default' as well
źródło
Nie musisz więc powtarzać kodu, jeśli potrzebujesz kilku przypadków, aby zrobić to samo:
case THIS: case THAT: { code; break; }
Możesz też:
case THIS: { do this; } case THAT: { do that; }
W sposób kaskadowy.
Naprawdę podatne na błędy / zamieszanie, jeśli o mnie chodzi.
źródło
do this
ido that
dla tego, ale tylkodo that
dla tego?Jeśli chodzi o zapis historyczny, Tony Hoare wynalazł opis przypadku w latach sześćdziesiątych XX wieku, podczas rewolucji „programowania strukturalnego”. Oświadczenie Tony'ego obsługiwało wiele etykiet na sprawę i automatyczne wyjście bez śmierdzących
break
oświadczeń. Wymóg wyraźnegobreak
był czymś, co wyszło z linii BCPL / B / C. Dennis Ritchie pisze (w ACM HOPL-II):Nie udało mi się znaleźć żadnych zapisów historycznych na temat BCPL, ale komentarz Ritchiego sugeruje, że
break
był to mniej więcej historyczny wypadek. BCPL później naprawił problem, ale być może Ritchie i Thompson byli zbyt zajęci wymyślaniem Uniksa, aby zawracać sobie głowę takim szczegółem :-)źródło
break
umożliwia „wykonanie wielu bloków kodu” i bardziej interesuje go motywacja tego wyboru projektu. Inni wspominali o dobrze znanym dziedzictwie od C do Java, a ta odpowiedź posunęła badania jeszcze dalej, do czasów sprzed C. Żałuję, że nie mamy tego (choć bardzo prymitywnego) dopasowania wzorców od samego początku.Java wywodzi się z języka C, którego dziedzictwo obejmuje technikę znaną jako urządzenie Duffa . Jest to optymalizacja polegająca na fakcie, że kontrola przechodzi z jednego przypadku do drugiego w przypadku braku
break;
instrukcji. Do czasu ujednolicenia języka C było mnóstwo takiego kodu „na wolności” i zmiana języka w celu złamania takich konstrukcji przyniosłaby efekt przeciwny do zamierzonego.źródło
Jak powiedzieli wcześniej ludzie, ma to na celu umożliwienie przebicia i nie jest to błąd, to cecha. Jeśli
break
denerwuje Cię zbyt wiele instrukcji, możesz łatwo się ich pozbyć, używającreturn
zamiast nich instrukcji. Jest to właściwie dobra praktyka, ponieważ twoje metody powinny być tak małe, jak to tylko możliwe (ze względu na czytelność i łatwość utrzymania), więcswitch
instrukcja jest już wystarczająco duża dla metody, dlatego dobra metoda nie powinna zawierać niczego więcej, to jest przykład: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"); } } }
Wykonanie drukuje:
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
zgodnie z oczekiwaniami.
źródło
Brak automatycznego przerwania dodanego przez kompilator umożliwia użycie przełącznika / przypadku do testowania warunków, takich jak
1 <= a <= 3
usunięcie instrukcji break z 1 i 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; }
źródło
ponieważ są sytuacje, w których chcesz przepłynąć przez pierwszy blok, na przykład, aby uniknąć pisania tego samego kodu w wielu blokach, ale nadal możesz je podzielić w celu kontroli mroe. Jest też mnóstwo innych powodów.
źródło
To stare pytanie, ale w rzeczywistości spotkałem się dzisiaj z używaniem instrukcji bez przerwy. Brak używania break jest w rzeczywistości bardzo przydatny, gdy trzeba połączyć różne funkcje po kolei.
np. użycie kodów odpowiedzi http do uwierzytelnienia użytkownika za pomocą tokena czasowego
kod odpowiedzi serwera 401 - token jest nieaktualny -> ponownie wygeneruj token i zaloguj użytkownika.
Kod odpowiedzi serwera 200 - token jest OK -> zaloguj się użytkownika.
w przypadku oświadczeń:
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; }
Korzystając z tego, nie musisz wywoływać funkcji logowania użytkownika w celu uzyskania odpowiedzi 401, ponieważ po ponownym wygenerowaniu tokenu środowisko wykonawcze przechodzi do przypadku 200.
źródło
Możesz łatwo oddzielić inny typ liczby, miesiąca, liczby.
Tak jest lepiej, jeśli w tym przypadku;
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; } }
źródło
Teraz pracuję nad projektem, w którym potrzebuję
break
w mojej instrukcji przełącznika, w przeciwnym razie kod nie zadziała. Trzymaj się ze mną, a dam ci dobry przykład tego, dlaczego potrzebujeszbreak
w oświadczeniu o przełączniku.Wyobraź sobie, że masz trzy stany, jeden, który czeka, aż użytkownik wprowadzi liczbę, drugi do obliczenia, a trzeci do wydrukowania sumy.
W takim przypadku masz:
Patrząc na stany, chciałbyś, aby kolejność egzekwowania zaczynała się od stan1 , następnie stan3 i na końcu stan2 . W przeciwnym razie wydrukujemy tylko dane wejściowe użytkowników bez obliczania sumy. Aby jeszcze raz to wyjaśnić, czekamy, aż użytkownik wprowadzi wartość, a następnie obliczamy sumę i wypisuje ją.
Oto przykładowy kod:
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; } }
Jeśli nie używamy
break
, będzie wykonywał się w tej kolejności, stan1 , stan2 i stan3 . Ale używającbreak
, unikamy tego scenariusza i możemy uporządkować we właściwej procedurze, która zaczyna się od state1, następnie state3 i na końcu state2.źródło
Dokładnie, ponieważ dzięki sprytnemu rozmieszczeniu możesz wykonywać bloki w kaskadzie.
źródło