È necessario aggiungere il caso predefinito durante l'utilizzo dei casi switch?


41

Durante una recente revisione del codice mi è stato chiesto di inserire defaultcasi in tutti i file ovunque switchvenga utilizzato il blocco, anche se non c'è nulla da fare default. Ciò significa che devo mettere il defaultcaso e non scrivere nulla al suo interno.

È questa la cosa giusta da fare? Quale scopo servirebbe?


9
Dipende da ... intendo lo stile di codice del supervisore.
SD

1
Penso che ci siano anche casi in cui non si desidera un caso predefinito. Considera di passare a un enum. Il tuo switch definisce un caso per tutti i possibili valori. Ora l'aggiunta di un valore a un enum dovrebbe comportare l'aggiunta di un caso in quel parametro. Se non lo hai fatto, questo potrebbe essere un errore, dal momento che vuoi fare qualcosa per questo nuovo valore enum, ma il tuo codice non lo fa. Il tuo compilatore può avvisarti (penso; non sono sicuro di Java) a riguardo, ma solo se non hai inserito un caso predefinito. In alternativa, potresti stampare un avviso nel caso predefinito quando sei sicuro che il codice non lo raggiungerà mai.
Sembra il

6
Io e i miei amici chiamiamo questo caso "Eccezione sviluppatore" - quando accade qualcosa nel codice che sai che non dovrebbe mai accadere (per motivi logici / di codice), quindi aggiungiamo un'eccezione "Sviluppatore". In questo modo, se ciò dovesse accadere, viene generata un'eccezione per gli sviluppatori e almeno sappiamo dove le cose sono andate gravemente male.
Marco

Risposte:


31

Sembra che ci siano tre casi in cui una defaultdichiarazione non è necessaria:

  1. non rimangono altri casi, perché esiste un set limitato di valori che immettono il file switch case. Ma questo potrebbe cambiare con il tempo (intenzionalmente o accidentalmente), e sarebbe bene avere un default casecambiamento di qualsiasi cosa _ potresti registrare o avvisare l'utente di un valore errato.

  2. sai come e dove switch caseverrà utilizzato e quali valori lo inseriranno. Ancora una volta, questo potrebbe cambiare e potrebbe essere necessaria un'elaborazione aggiuntiva.

  3. altri casi non necessitano di particolari elaborazioni. In questo caso, penso che ti venga chiesto di aggiungere un default case, perché è uno stile di codifica accettato e rende il tuo codice più leggibile.

I primi due casi si basano su ipotesi. Quindi (supponendo che tu lavori in un team non così piccolo dato che hai regolari revisioni del codice), non puoi permetterti di fare quei presupposti. Non sai chi lavorerà con il tuo codice o effettuerà chiamate a funzioni / invocando metodi nel tuo codice. Allo stesso modo, potresti dover lavorare con il codice di qualcun altro. Avere lo stesso stile di codifica renderà più semplice la gestione del codice di qualcuno (incluso il tuo).


14
Nel caso 1 e 2, un caso predefinito dovrebbe essere un errore. Se il tuo stile di codifica usa sempre il valore predefinito, allora fallo, ma fallo emettere un errore in 1 e 2. È possibile, dovrebbe emetterlo in fase di compilazione, ma potrebbe non essere sempre possibile, se mai in java. Almeno penso che sia importante che l'utente riceva il messaggio "Ti stai sparando ai piedi passando questo valore"
martiert

11
Un caso predefinito è sempre una buona idea, perché, ad esempio nel caso di un enum, se qualcuno aggiunge una nuova voce all'enum, il tuo caso predefinito dovrebbe fare qualcosa di simile a quello throw new IllegalStateException("Unrecognised case "+myEnum)che ti mostra dove e qual è l'errore.
Rich

Questo significa che dovresti sempre avere una elseclausola dopo ogni, if/else ifanche se sai che non dovrebbe accadere? Non mi sembra una buona idea ...
jadkik94

2
Se posso aggiungere un aneddoto dal mio lavoro: avevo un metodo factory che utilizzava un enum per creare un'istanza di alcune classi. Avevo lasciato istruzioni che ogni volta che aggiungevi una nuova classe a quel progetto dovevi aggiungere un valore all'enum e un caso allo switch. Certo, una settimana dopo aver chiuso il codice smette di funzionare, con una delle eccezioni che non devono mai essere sollevate. Vado dal programmatore e gli chiedo: "hai aggiunto un caso allo switch?" e sicuramente, non lo fece. In quel giorno ho cambiato l'eccezione per dire "se ricevi questo messaggio, incolpare l'ultimo sviluppatore che ha toccato il codice".
Ziv

28

È questo il modo giusto di fare? Quale scopo servirebbe?

Non è raro che gli standard di codifica aziendale richiedano un caso predefinito per tutte le switchdichiarazioni. Uno dei motivi è che rende facile per i lettori trovare la fine del switch. Un'altra ragione, probabilmente migliore, è che ti fa riflettere per un secondo su cosa dovrebbe fare il tuo codice quando la condizione non corrisponde alle tue aspettative. Indipendentemente dal motivo del requisito, se si tratta di uno standard aziendale, è necessario seguirlo a meno che non vi sia un motivo ermetico per non farlo.

Se ritieni che switchincluda i casi per ogni possibile condizione, una buona cosa da fare è inserire assertun'istruzione nel caso predefinito. In questo modo, quando qualcuno modifica il codice e aggiunge inavvertitamente una condizione che la tua switchnon copre, colpirà asserte realizzerà che deve affrontare quella parte del codice.

Se il tuo switchcopre solo alcune delle possibili condizioni, ma nulla di speciale deve essere fatto per le altre, puoi lasciare vuoto il caso predefinito. È una buona idea aggiungere un commento in quel caso per indicare che il caso predefinito è intenzionalmente vuoto perché le condizioni che lo colpiscono non richiedono alcun lavoro da eseguire.


1
che cos'è un assert?
VOLA

1
@FLY È una parola chiave in Java e di solito una macro in C, C ++, ecc. Ad ogni modo, un'asserzione viene utilizzata per verificare che alcune ipotesi rimangano vere e generino eccezioni o causino un errore in caso contrario.
Caleb,

Ho modificato il mio commento precedente con un link a Wikipedia
FLY

Non dovresti lasciare un caso vuoto. Se il tuo switch non copre tutte le possibili condizioni, devi affermare le altre condizioni nel tuo caso predefinito per lo stesso motivo per cui hai suggerito di aggiungere un'asserzione nel caso predefinito nel tuo secondo paragrafo.
rold2007

12

Se si "attiva" il tipo di enumerazione puro, è pericoloso avere un fallback predefinito. Quando successivamente si aggiungono valori al tipo di enumerazione, il compilatore evidenzia le opzioni con nuovi valori non coperti. Se hai una clausola predefinita lì, il compilatore rimarrà silenzioso e potresti perderlo.


1
+1 per la risoluzione del problema in fase di compilazione e non in fase di esecuzione.
SylvainD,

Mi chiedo come la risposta completamente fuorviante sia votata e accettata. Googling rapido mostra che il compilatore java ha questo tipo di avviso. Perché mai aspetterai un errore in fase di esecuzione, gente?
Artur,

Non sapevo di questo comportamento. Il mio esempio di codice ideone ideone.com/WvytWq suggerisce il contrario. Supponendo che tu dica che è vero, cosa succede se qualcuno aggiunge valori a un tipo di enumerazione, ma non ricompili il tuo codice?
emory


1
Ovviamente, i nuovi valori non sono gestiti dall'istruzione switch senza il caso predefinito. Ma se non ricompili le tue implementazioni quando cambiano le interfacce, il tuo sistema di compilazione è davvero rotto.
Artur,

9

Per molti aspetti, questa domanda è la stessa della domanda frequente Ho bisogno di una elseclausola alla fine di una if/ else ifscala che copre ogni opzione .

La risposta è, sintatticamente parlando, no, non è così. Ma c'è un ...

Una defaultclausola può essere presente per (almeno) due motivi:

  1. Come gestore di errori - certo, non dovrei mai arrivare qui! è un'affermazione che può essere fatta, ma se fosse così? La corruzione dei dati, o peggio ancora, nessuna convalida dei dati sono percorsi per il fallimento del programma se non correttamente intrappolati. In questo caso, non dovrebbe essere una clausola vuota!
  2. Copertura di progettazione / codice - anche nella forma più semplice di un diagramma di flusso, ci sono due percorsi da un'istruzione if e sempre otherwiseda un caso. Non vi è alcun motivo per non includerli nel codice sorgente.

La mia filosofia è sempre abbastanza semplice: valutare lo scenario peggiore delle due opzioni e scegliere il più sicuro. Nel caso di una clausola vuota elseo default, le opzioni peggiori sono:

  • Includili: altre tre o quattro righe di codice "ridondante".
  • Non includerli: i dati non autorizzati o una condizione imprevista non vengono intrappolati, causando potenzialmente un errore del programma.

Overdramatic? Forse ... ma poi il mio software ha il potenziale di uccidere le persone se va storto. Preferirei non correre questo rischio.


A parte questo, le linee guida MISRA-C {vedi profilo per affiliazione} raccomandano una defaultclausola per ogniswitch


Non solo alla fine di una scala. La domanda è: ho bisogno di un elsedopo un if. Ma come hai detto, no, non lo è.
ott--

Come gestore degli errori, suggerisco un modulo leggermente modificato che evita il default vuoto. Per quanto riguarda l'uccisione di persone, non penso che sia davvero importante: le persone saranno altrettanto morte indipendentemente dal fatto che tu abbia o meno la clausola predefinita, ma renderà più facile capire perché sono morti.
emory

Buon punto, @emory - non era molto chiaro, era ... modificato :)
Andrew

6

Java non ti obbliga ad avere un'istruzione "predefinita", ma è buona norma averne sempre una, anche se il codice potrebbe non essere mai raggiunto (in questo momento). Ecco alcuni motivi:

Avendo una clausola di default irraggiungibile, mostri al lettore del tuo codice che hai considerato i valori e sai cosa stai facendo. Consenti anche modifiche future, ad esempio: viene aggiunto un nuovo valore enum, l'opzione non dovrebbe ignorare silenziosamente il nuovo valore; puoi invece lanciare un'eccezione o fare qualcos'altro.

Per ottenere un valore imprevisto (nel caso in cui non si stia attivando un enum) che viene passato, potrebbe essere maggiore o minore di quello previsto, ad esempio.

Per gestire le azioni "predefinite" - dove gli switch sono per comportamenti speciali. Ad esempio, una variabile potrebbe essere dichiarata all'esterno dell'interruttore ma non inizializzata e ogni caso la inizializza in qualcosa di diverso. L'impostazione predefinita in questo caso potrebbe inizializzarlo su un valore predefinito in modo che il codice immediatamente dopo lo switch non si verifichi / generi un'eccezione.

Anche se decidi di non inserire nulla nel valore predefinito (nessuna eccezione, registrazione, ecc.), Anche un commento per dire che hai considerato che il fatto predefinito non si verificherà mai può aiutare la leggibilità del tuo codice; ma ciò dipende dalle preferenze personali.


2

Vorrei anche aggiungere che dipende dalla filosofia della lingua che stai usando. Ad esempio, in Erlang, dove si tratta di un approccio comune per "lasciarlo andare in crash", non si definirebbe un caso predefinito ma si lascerebbe che la propria casedichiarazione generasse un errore se si verifica una situazione non soddisfatta.


0

Dovresti sempre avere un valore predefinito a meno che tu non abbia coperto in modo dimostrabile e permanente tutti i casi. Non costa nulla ed è un'assicurazione contro il fallimento nel mantenere la tua dichiarazione di cambio in un programma in evoluzione.

Se sei davvero sicuro di non preoccuparti di nessun altro caso, il default: break; // non importa ma il default predefinito dovrebbe essere qualcosa come default: generate new Error ("valore inatteso");

Allo stesso modo, non dovresti mai "scorrere" un costrutto di caso non banale senza aggiungere un commento all'effetto che intendi eliminare. Java ha sbagliato questo in fase di progettazione.


-2

Ritenere

enum Gender
{
       MALE ,
       FEMALE ;
}

e

void run ( Gender g )
{
      switch ( g )
      {
           case MALE :
                 // code related to males
                 break ;
           default :
                 assert Gender . FEMALE . equals ( g ) ;
                 // code related to females
                 break ;
      }
}

Direi che questo è superiore all'avere un caso MASCHIO, un caso FEMMINA e un caso predefinito che genera un'eccezione.

Durante la fase di test, il computer verificherà se g è FEMMINA. Durante la produzione, il controllo verrà omesso. (Sì, una microottimizzazione!)

Ancora più importante, manterrai il tuo obiettivo di copertura del codice al 100%. Se hai scritto un caso predefinito che genera un'eccezione, come lo testeresti mai?


1
1. Nessuna necessità di microottimizzazione: l'eliminazione del codice morto viene utilizzata nei compilatori negli ultimi 30 anni circa e sarà eliminata da JIT. 2. La copertura del codice al 100% di solito non è considerata importante (da un lato la copertura del 100% potrebbe non rilevare tutti i casi limite, dall'altro nessun test rileverà le righe assert_not_reached sul programma corretto). In questo caso, non lasciare alcun caso predefinito causerebbe un errore del compilatore. D'altra parte, come verifichi se l'asserzione funziona (sposta il problema e lo nascondi al 100% di copertura invece di avere la stessa linea "non accadrà, lo prometto").
Maciej Piechotka,

1
3. Quando Gender . FEMALE . equals ( FEMALE ) ;valuta false? Gender.FEMALE.equals (g)Volevi dire, ma dato che puoi dipendere dal fatto che i riferimenti agli enum siano stabili, puoi semplicemente scrivere g == Gender.FEMALE. 4. (Opinione personale) tale codice è molto più difficile da leggere e produrrà errori leggermente più confusi.
Maciej Piechotka,

@MaciejPiechotka buona cattura di g. Sì, la microottimizzazione non è un vantaggio, ma non è nemmeno uno svantaggio. La copertura del codice al 100% è un vantaggio se questo è uno dei tuoi obiettivi e il tuo strumento di copertura del codice non controlla le affermazioni (cosa che non dovrebbe).
emory

Allora perché dovrebbe essere uno dei miei obiettivi? La parte "interessante" dovrebbe essere testata per tutti i casi limite (indipendentemente dal fatto che si associno a righe di codice) e le parti "noiose" possono essere semplicemente ignorate per risparmiare tempo nei test. La copertura del codice IMHO è una cattiva metrica dei test.
Maciej Piechotka,
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.