Risposte:
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 è un cattivo stile "fallire" e avere più blocchi di codice eseguiti per un caso, ma in alcune situazioni potrebbero esserci degli usi.
// Intentional fallthrough.
quando ometti una pausa. Non è tanto un cattivo stile quanto "è facile dimenticare una pausa accidentalmente" secondo me. PS Ovviamente non in casi semplici come nella risposta stessa.
case
impilati insieme in questo modo. Se c'è del codice tra di loro, allora sì, il commento è probabilmente meritato.
case
, in questo modo case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();
:, senza bisogno di una pausa tra i casi. Pascal potrebbe farlo: "L'istruzione case confronta il valore dell'espressione ordinale con ciascun selettore, che può essere una costante, un sottointervallo o un elenco di essi separati da virgole." ( wiki.freepascal.org/Case )
Storicamente , è perché case
essenzialmente definiva un label
, noto anche come il punto di destinazione di una goto
chiamata. L'istruzione switch ei casi associati rappresentano in realtà solo un ramo a più vie con più potenziali punti di ingresso in un flusso di codice.
Detto questo, è stato notato un numero quasi infinito di volte che break
è quasi sempre il comportamento predefinito che preferiresti avere alla fine di ogni caso.
Java viene da C e questa è la sintassi di C.
Ci sono momenti in cui desideri che più istruzioni case abbiano un solo 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);
}
}
Penso che sia un errore. In quanto costrutto di linguaggio, è altrettanto facile da avere break
come predefinito e invece avere una fallthrough
parola chiave. La maggior parte del codice che ho scritto e letto ha un'interruzione dopo ogni caso.
continue <case name>
quale opzione consente di specificare esplicitamente con quale istruzione case continuare;
case
all'interno della corrente switch
, questo diventa semplicemente un file goto
. ;-)
Puoi fare ogni sorta di cose interessanti con il fall-through del caso.
Ad esempio, supponiamo che tu voglia eseguire un'azione particolare per tutti i casi, ma in un determinato caso desideri eseguire quell'azione più qualcos'altro. Usare un'istruzione switch con fall-through lo renderebbe abbastanza 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' break
affermazione alla fine di un caso e provocare un comportamento inaspettato. I buoni compilatori ti avviseranno quando ometti l'istruzione break.
Perché il compilatore non inserisce automaticamente le istruzioni break dopo ogni blocco di codice nello switch?
Tralasciando la buona volontà di poter utilizzare lo stesso blocco per più casi (che potrebbero essere casi speciali) ...
È per ragioni storiche? Quando vuoi che vengano eseguiti più blocchi di codice?
È principalmente per compatibilità con C, ed è probabilmente un antico trucco dei tempi antichi, quando le goto
parole chiave vagavano per la terra. Si fa abilitare alcune cose incredibili, naturalmente, come il dispositivo di Duff , ma se questo è un punto a suo favore o contro è ... polemico al meglio.
Il break
after switch case
s viene utilizzato per evitare il fallthrough nelle istruzioni switch. Anche se è interessante notare che ora questo può essere ottenuto attraverso le etichette degli interruttori di nuova formazione implementate tramite JEP-325 .
Con queste modifiche, il break
con ogni interruttore case
può essere evitato come dimostrato ulteriormente: -
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");
}
}
}
Sulla esecuzione del codice precedente con JDK-12 , l' uscita comparativa potrebbe essere visto come
//input
1
// output from the implementation before JEP-325
one
two
many
// output from the implementation after JEP-325
one
e
//input
2
// output from the implementation before JEP-325
two
many
// output from the implementation after JEP-325
two
e ovviamente la cosa è rimasta invariata
// input
3
many // default case match
many // branches to 'default' as well
Quindi non devi ripetere il codice se hai bisogno di più casi per fare la stessa cosa:
case THIS:
case THAT:
{
code;
break;
}
Oppure puoi fare cose come:
case THIS:
{
do this;
}
case THAT:
{
do that;
}
In modo a cascata.
Veramente incline a bug / confusione, se me lo chiedi.
do this
e do that
per questo, ma solo do that
per questo?
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 supportava più etichette per caso e l'uscita automatica senza break
dichiarazioni puzzolenti . Il requisito per un esplicito break
era qualcosa che proveniva dalla linea BCPL / B / C. Dennis Ritchie scrive (in ACM HOPL-II):
Ad esempio, il caso finale che sfugge a un'istruzione switchon BCPL non era presente nella lingua quando l'abbiamo appresa negli anni '60, quindi il sovraccarico della parola chiave break per sfuggire all'istruzione switch B e C è dovuto a un'evoluzione divergente piuttosto che cosciente modificare.
Non sono stato in grado di trovare alcun scritto storico su BCPL, ma il commento di Ritchie suggerisce che sia break
stato più o meno un incidente storico. BCPL in seguito ha risolto il problema, ma forse Ritchie e Thompson erano troppo impegnati a inventare Unix per preoccuparsi di un simile dettaglio :-)
break
consente "l'esecuzione di più blocchi di codice", ed è più interessato alla motivazione di questa scelta progettuale. Altri hanno menzionato la ben nota eredità da C a Java, e questa risposta ha spinto ulteriormente la ricerca fino ai giorni pre-C. Vorrei che avessimo questo pattern (sebbene molto primitivo) sin dall'inizio.
Java è derivato 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 break;
un'istruzione. Quando il C fu standardizzato, c'era un sacco di codice come quello "in natura" e sarebbe stato controproducente cambiare il linguaggio per rompere tali costruzioni.
Come si diceva prima, è per consentire il fall-through e non è un errore, è una caratteristica. Se troppe break
affermazioni ti infastidiscono, puoi facilmente sbarazzartene usando le return
dichiarazioni. Questa è in realtà una buona pratica, perché i tuoi metodi dovrebbero essere il più piccoli possibile (per motivi di leggibilità e manutenibilità), quindi switch
un'istruzione è già abbastanza grande per un metodo, quindi, un buon metodo non dovrebbe contenere nient'altro, questo è 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.
Non avere un'interruzione automatica aggiunta dal compilatore rende possibile utilizzare un interruttore / caso per testare 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;
}
È una vecchia domanda, ma oggi mi sono imbattuto nell'usare il caso senza dichiarazione di interruzione. Non usare break è in realtà molto utile quando è necessario combinare diverse funzioni in sequenza.
ad esempio utilizzando i codici di risposta http per autenticare l'utente con il time token
codice di risposta del server 401 - il token è obsoleto -> rigenera il token e accede l'utente.
codice di risposta del server 200 - il token è OK -> effettua il login dell'utente.
in caso di dichiarazioni:
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 chiamare la funzione utente di accesso per la risposta 401 perché quando il token viene rigenerato, il runtime salta nel caso 200.
Puoi facilmente separare altri tipi di numero, mese, conteggio.
È meglio quindi 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;
}
}
Ora sto lavorando al progetto di cui ho bisogno break
nella mia istruzione switch, altrimenti il codice non funzionerà. Abbi pazienza e ti darò un buon esempio del motivo per cui hai bisogno break
nella tua dichiarazione di cambio.
Immagina di avere tre stati, uno che attende che l'utente inserisca un numero, il secondo per calcolarlo e il terzo per stampare la somma.
In tal caso hai:
Guardando gli stati, vorresti che l'ordine di esazione inizi su state1 , poi su state3 e infine su state2 . Altrimenti stamperemo solo gli input degli utenti senza calcolare la somma. Giusto per chiarirlo di nuovo, aspettiamo che l'utente inserisca un valore, quindi calcoliamo la somma e stampiamo la somma.
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 break
, che eseguirà in questo ordine, stato1 , Stato2 e state3 . Ma usando break
, evitiamo questo scenario e possiamo ordinare nella giusta procedura che è iniziare con state1, poi state3 e, ultimo ma non meno importante, state2.
break
.