Perché abbiamo bisogno di una pausa dopo le dichiarazioni del caso?


Risposte:


94

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.


28
Basta aggiungere sempre un commento lungo le linee // 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.
doublep

@doublep - Sono d'accordo. Secondo me, lo eviterei se possibile, ma se ha senso assicurati che sia molto chiaro quello che stai facendo.
WildCrustacean

6
@doublep: non mi preoccuperei del commento se i multipli fossero caseimpilati insieme in questo modo. Se c'è del codice tra di loro, allora sì, il commento è probabilmente meritato.
Billy ONeal

4
Immagino un linguaggio in cui puoi dichiarare più casi all'interno di uno 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 )
Christian Semrau

32

Storicamente , è perché caseessenzialmente definiva un label, noto anche come il punto di destinazione di una gotochiamata. 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.


30

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

4
Qualcuno ha mirato alla freccia su e l'ha mancato? O forse hanno avuto un problema con il tuo stile di parentesi graffa o indentazione ...
Jim Lewis

Boh, quindi dammi un +1. Questo è un esempio in cui il fall-through aiuta, anche se vorrei davvero che Java avesse scelto un'istruzione case più moderna. VALUTA ALTRIMENTI di Cobol è molto più potente e precede Java. L'espressione match di Scala è un esempio moderno di ciò che si potrebbe fare.
Jim Ferrans

1
I miei studenti sarebbero stati fustigati pubblicamente per aver fatto questo. È brutto coyote.
ncmathsadist

2
@ncmathsadist Illustra un punto su un modo di fare qualcosa. Non sono d'accordo sul fatto che questo esempio sia probabilmente estremo. Ma è un esempio del mondo reale, che credo aiuti le persone a comprendere un concetto.
Romain Hippeau

15

Penso che sia un errore. In quanto costrutto di linguaggio, è altrettanto facile da avere breakcome predefinito e invece avere una fallthroughparola chiave. La maggior parte del codice che ho scritto e letto ha un'interruzione dopo ogni caso.


4
Preferisco suggerire continue <case name>quale opzione consente di specificare esplicitamente con quale istruzione case continuare;
Vilx

4
@Vilx Quando si consente un arbitrario caseall'interno della corrente switch, questo diventa semplicemente un file goto. ;-)
Christian Semrau

13

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' breakaffermazione alla fine di un caso e provocare un comportamento inaspettato. I buoni compilatori ti avviseranno quando ometti l'istruzione break.


È possibile utilizzare switch / case sulle stringhe in Java?
Steve Kuo

@ Steve: Oops, credo di no al momento. Secondo stackoverflow.com/questions/338206/… , le stringhe saranno consentite in una futura versione di Java. (Attualmente eseguo la maggior parte della mia programmazione in C #, che consente le stringhe nelle istruzioni switch.) Ho modificato la risposta per rimuovere le virgolette fuorvianti.
Zach Johnson,

2
@ZachJohnson, molto più tardi, Java 7 consente di attivare le stringhe.
Bob Cross

7

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 gotoparole 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.


5

Il breakafter switch cases 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 breakcon ogni interruttore casepuò 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

4

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.


fa che corrono sia do thise do thatper questo, ma solo do thatper questo?
JonnyRaa

1
basta leggere i documenti. È terribile! Che modo semplice per scrivere bug!
JonnyRaa

4

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 breakdichiarazioni puzzolenti . Il requisito per un esplicito breakera 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 breakstato 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 :-)


Questo dovrebbe ottenere più voti. Apparentemente l'OP sapeva già che l'omissione breakconsente "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.
wlnirvana

3

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.


1

Come si diceva prima, è per consentire il fall-through e non è un errore, è una caratteristica. Se troppe breakaffermazioni ti infastidiscono, puoi facilmente sbarazzartene usando le returndichiarazioni. Questa è in realtà una buona pratica, perché i tuoi metodi dovrebbero essere il più piccoli possibile (per motivi di leggibilità e manutenibilità), quindi switchun'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.


0

Non avere un'interruzione automatica aggiunta dal compilatore rende possibile utilizzare un interruttore / caso per testare condizioni come 1 <= a <= 3rimuovendo 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;
}

Yecch. Lo odio totalmente.
ncmathsadist

0

perché ci sono situazioni in cui vuoi fluire attraverso il primo blocco, ad esempio per evitare di scrivere lo stesso codice in più blocchi, ma essere comunque in grado di dividerli per un maggiore controllo. Ci sono anche un sacco di altri motivi.


0

È 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.


0

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

0

Ora sto lavorando al progetto di cui ho bisogno breaknella mia istruzione switch, altrimenti il ​​codice non funzionerà. Abbi pazienza e ti darò un buon esempio del motivo per cui hai bisogno breaknella 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:

  1. State1 - Attendi che l'utente inserisca un numero
  2. State2 - Stampa la somma
  3. state3 - Calcola la somma

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.


-1

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.