Perché abbiamo bisogno di dichiarazioni break after case?

Perché il compilatore non inserisce automaticamente le istruzioni di interruzione dopo ogni blocco di codice nello switch? È per ragioni storiche? Quando vuoi eseguire più blocchi di codice?

A volte è utile avere più casi associati allo stesso blocco di codice, ad esempio

case 'A': case 'B': case 'C': doSomething(); break; case 'D': case 'E': doSomethingElse(); break; 

ecc. Solo un esempio.

Nella mia esperienza, di solito è uno stile infetto “fallire” e si eseguono più blocchi di codice per un caso, ma in alcune situazioni potrebbero esserci degli usi.

Storicamente , è perché il case stava essenzialmente definendo label , anche conosciuta come il punto di arrivo di una chiamata goto . L’istruzione switch e i suoi casi associati rappresentano in realtà solo un ramo a più vie con più potenziali punti di ingresso in un stream di codice.

Detto questo, è stato notato un numero quasi infinito di volte in cui la break è quasi sempre il comportamento predefinito che preferiresti avere alla fine di ogni caso.

Java deriva da C e questa è la syntax di C.

Vi sono momentjs in cui si desidera che più istruzioni case abbiano solo un percorso di esecuzione. Di seguito è riportato un esempio che ti dirà quanti giorni in un mese.

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

Puoi fare ogni sorta di cose interessanti con il caso fall-through.

Ad esempio, diciamo che vuoi fare una determinata azione per tutti i casi, ma in un certo caso vuoi fare quell’azione più qualcos’altro. L’uso di un’istruzione switch con fall-through lo renderebbe piuttosto semplice.

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

Naturalmente, è facile dimenticare l’istruzione break alla fine di un caso e causare un comportamento imprevisto. I buoni compilatori ti avviseranno quando ometti la frase break.

Penso che sia un errore. Come costrutto linguistico è altrettanto facile avere break di default e invece avere una parola chiave fallthrough . La maggior parte del codice che ho scritto e letto ha una pausa dopo ogni caso.

Perché il compilatore non inserisce automaticamente le istruzioni di interruzione dopo ogni blocco di codice nello switch?

Tralasciando il buon desiderio di poter usare il blocco identico per diversi casi (che potrebbero essere speciali) …

È per ragioni storiche? Quando vuoi eseguire più blocchi di codice?

È principalmente per compatibilità con C, ed è probabilmente un antico hack dai tempi antichi quando le parole chiave goto vagavano per la terra. Ovviamente abilita alcune cose incredibili, come Duff’s Device , ma se questo è un punto a favore o contro è … argomentativo al meglio.

Quindi non devi ripetere il codice se hai bisogno di più casi per fare la stessa cosa:

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

O puoi fare cose come:

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

In una moda a cascata.

Davvero bug / confusione incline, se me lo chiedi.

Java deriva da C, la cui eredità include una tecnica nota come Duff’s Device . È un’ottimizzazione che si basa sul fatto che il controllo passa da un caso all’altro, in assenza di una break; dichiarazione. Quando C fu standardizzato, ci fu un sacco di codice come quello “in the wild”, e sarebbe stato controproducente cambiare il linguaggio per rompere tali costruzioni.

Per quanto riguarda il record storico, Tony Hoare ha inventato la dichiarazione del caso negli anni ’60, durante la rivoluzione della “programmazione strutturata”. La dichiarazione del caso di Tony ha supportato più etichette per caso e l’uscita automatica senza istruzioni di break puzzolente. Il requisito per un’interruzione esplicita era qualcosa che usciva dalla linea BCPL / B / C. Dennis Ritchie scrive (in ACM HOPL-II):

Ad esempio, il endcase che sfugge a un’istruzione switch BCPL non era presente nel linguaggio quando lo abbiamo imparato negli anni ’60, e quindi l’overloading della parola chiave break per uscire dall’istruzione switch B e C deve un’evoluzione divergente piuttosto che cosciente modificare.

Non sono stato in grado di trovare scritti storici sulla BCPL, ma il commento di Ritchie suggerisce che la break stata più o meno un incidente storico. BCPL ha successivamente risolto il problema, ma forse Ritchie e Thompson erano troppo occupati a inventare Unix per essere infastiditi da un tale dettaglio 🙂

Non avere un’interruzione automatica aggiunta dal compilatore rende ansible utilizzare un interruttore / caso per verificare condizioni come 1 <= a <= 3 rimuovendo l'istruzione break da 1 e 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; } 

perché ci sono situazioni in cui si desidera attraversare il primo blocco, ad esempio per evitare di scrivere lo stesso codice in più blocchi, ma essere comunque in grado di dividerli per il controllo mroe. Ci sono anche una tonnellata di altri motivi.

È una vecchia domanda, ma in realtà mi sono imbattuto nell’uso del caso senza una dichiarazione di rottura oggi. Non usare l’interruzione è in realtà molto utile quando è necessario combinare diverse funzioni in sequenza.

ad esempio utilizzando i codici di risposta http per autenticare l’utente con un token temporale

codice di risposta del server 401 – token non aggiornato -> rigenera token e registra utente in.
codice di risposta del server 200 – il token è OK -> registra utente in.

in caso affermazioni:

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

Usando questo non è necessario richiamare la funzione utente di login per la risposta 401 perché quando il token viene rigenerato, il runtime salta nel caso 200.

Puoi fare facilmente per separare un altro tipo di numero, mese, contare.
Questo è meglio allora se in questo caso;

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

Come hanno detto in precedenza, è per consentire il fall-through e non è un errore, è una caratteristica. Se troppe dichiarazioni di break ti infastidiscono, puoi facilmente eliminarle usando invece le dichiarazioni di return . Questa è in realtà una buona pratica, perché i tuoi metodi dovrebbero essere il più ansible piccoli (per motivi di leggibilità e manutenibilità), quindi un’istruzione switch è già abbastanza grande per un metodo, quindi un buon metodo non dovrebbe contenere nient’altro, è un esempio:

 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’esecuzione stampa:

 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 

come previsto.

Ora sto lavorando a un progetto in cui ho bisogno di break mia dichiarazione di switch altrimenti il ​​codice non funzionerà. Resta con me e ti darò un buon esempio del perché hai bisogno di break tua dichiarazione di switch.

Immagina di avere tre stati, uno che aspetta che l’utente inserisca un numero, il secondo che lo calcoli e il terzo che stampi la sum.

In tal caso hai:

  1. Stato1 : attendere che l’utente inserisca un numero
  2. Stato2 – Stampa la sum
  3. state3 – Calcola la sum

Osservando gli stati, si vorrebbe che l’ordine di esazione inizi su state1 , quindi state3 e infine state2 . Altrimenti, stamperemo solo gli input degli utenti senza calcolare la sum. Giusto per chiarirlo nuovamente, attendiamo che l’utente inserisca un valore, quindi calcoli la sum e stampi la sum.

Ecco un codice di esempio:

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

Se non usiamo l’ break , verrà eseguito in questo ordine, stato1 , stato2 e stato3 . Ma usando l’ break , evitiamo questo scenario e possiamo ordinare nella giusta procedura che deve iniziare con state1, quindi state3 e last but not least state2.

Esatto, perché con un posizionamento intelligente puoi eseguire blocchi in cascata.