L'istruzione switch in Java
Usa switch in Java per confrontare un valore con più casi, con etichette case, break, default e il comportamento fall-through.
Quando devi confrontare un singolo valore con molte possibilità, una lunga catena if/else if diventa rapidamente caotica. L'istruzione switch è l'alternativa compatta di Java — legge il valore una volta, salta al case corrispondente ed esegue il suo blocco.
Sintassi di base
switch (value) {
case label1:
// body
break;
case label2:
// body
break;
default:
// body
break;
}Un piccolo esempio:
int day = 3;
switch (day) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
case 3:
System.out.println("Wednesday");
break;
default:
System.out.println("Other");
break;
}Lo switch salta a case 3:, stampa Wednesday, incontra break ed esce.
Il break è essenziale
Senza break, l'esecuzione cade in avanti al caso successivo — anche se la sua etichetta non corrisponde. Questa è una funzionalità deliberata dello switch in stile C, ma è la fonte di innumerevoli bug nel codice Java:
switch (day) {
case 1:
System.out.println("Monday");
// no break!
case 2:
System.out.println("Tuesday");
break;
}Quando day == 1, questo stampa sia Monday che Tuesday. Aggiungi sempre break a meno che tu non voglia intenzionalmente il fall-through.
Fall-through intenzionale
A volte il fall-through è esattamente quello che vuoi — raggruppare più etichette sotto lo stesso blocco:
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("Weekday");
break;
case 6:
case 7:
System.out.println("Weekend");
break;
}Quando lo fai intenzionalmente, aggiungi un commento // fall through affinché i revisori non pensino che tu abbia dimenticato un break. (La nuova sintassi delle switch expression — trattata nel prossimo capitolo — elimina completamente questo problema.)
Quali valori può accettare uno switch?
Un switch tradizionale accetta:
- Tutti i tipi interi:
byte,short,int,char - I loro wrapper boxed:
Byte,Short,Integer,Character String(da Java 7)- Costanti
enum
Non sono ammessi: long, float, double, boolean o oggetti arbitrari. Per questi, usa if/else.
String role = "admin";
switch (role) {
case "admin":
System.out.println("Full access");
break;
case "editor":
System.out.println("Write access");
break;
case "viewer":
System.out.println("Read access");
break;
default:
System.out.println("No access");
break;
}Il confronto tra stringhe in uno switch usa la semantica di String.equals — è sensibile alle maiuscole. Un punto critico: usare uno switch su una String (o qualsiasi valore boxed/enum) che è null lancia una NullPointerException. Controlla il null prima dello switch oppure gestiscilo in una guardia:
if (role == null) {
System.out.println("No role");
} else {
switch (role) {
case "admin":
System.out.println("Full access");
break;
// ...
}
}default — il caso generale
default viene eseguito quando nessun case corrisponde. Non è obbligatorio, ma includerlo è una buona pratica: rende esplicito il comportamento per valori inattesi.
default non deve necessariamente essere in fondo. Per convenzione va in basso, ma il compilatore lo accetta ovunque — l'esecuzione vi cade come in qualsiasi altro case se dimentichi break.
Regole da tenere a mente
Un paio di regole imposte dal compilatore possono sorprendere:
- Le etichette case devono essere costanti note a tempo di compilazione. Puoi usare letterali (
case 3:), costantifinalo nomi dienum— ma non una variabile o una chiamata a metodo.case x:dovexè una variabile non-final non compila. - Le etichette devono essere univoche. Due voci
case 3:nello stesso switch sono un errore di compilazione. - Tutti i case condividono un unico scope. Una variabile dichiarata in un case è visibile negli altri, il che può causare conflitti. Racchiudi il corpo di un case tra
{ }quando hai bisogno di una variabile locale con scope limitato a quel case:
switch (day) {
case 1: {
int hours = 8;
System.out.println(hours);
break;
}
case 2:
// `hours` is not visible here
break;
}Switch su enum
enum e switch sono una coppia naturale. All'interno di uno switch su un valore enum, non devi qualificare il nome della costante:
enum Status { PENDING, ACTIVE, DONE }
Status s = Status.ACTIVE;
switch (s) {
case PENDING: // not Status.PENDING
System.out.println("Waiting...");
break;
case ACTIVE:
System.out.println("In progress");
break;
case DONE:
System.out.println("Finished");
break;
}Un esempio pratico
Cosa c'è dopo
Java 14 ha introdotto le switch expression, che restituiscono un valore, eliminano il fall-through e supportano case con più etichette — il codice Java moderno le preferisce quando si lavora con Java 14 o versioni successive.