Espressioni switch in Java
Espressioni switch moderne in Java con sintassi a freccia, yield e pattern matching esaustivo.
Java 14 ha promosso le espressioni switch a funzionalità standard del linguaggio. Si basano sull'istruzione switch tradizionale con tre importanti miglioramenti: producono un valore, utilizzano una sintassi a freccia più pulita e rendono impossibile il fall-through. Se stai usando Java 14 o versioni successive, preferiscile.
Questo capitolo tratta la sintassi a freccia, la parola chiave yield per i corpi a blocco, il controllo di esaustività e il pattern matching basato sul tipo diventato standard in Java 21. Al termine saprai quando ricorrere a un'espressione switch invece di una catena if/else.
Istruzione vs. espressione
Il switch tradizionale è un'istruzione: fa qualcosa (esegue effetti collaterali) ma non produce un valore. Un'espressione switch invece restituisce un valore che puoi assegnare, ritornare o passare a un metodo. Questa singola differenza guida tutto il resto in questa pagina — poiché il risultato viene utilizzato, il compilatore può esigere che ogni ramo produca un valore e che nessun input passi inosservato.
Sintassi a freccia
String day = "TUE";
String label = switch (day) {
case "MON", "TUE", "WED", "THU", "FRI" -> "weekday";
case "SAT", "SUN" -> "weekend";
default -> "unknown";
};Tre cose da notare:
- L'intero
switchè un'espressione — restituisce un valore che puoi assegnare (nota il;finale). - Ogni case usa
->invece di:. Il lato destro è un'istruzione o espressione singola. - Sono consentite più etichette in un case, separate da virgola. Nessun
break, nessun fall-through.
Corpi a blocco con yield
Se un case richiede più istruzioni, usa un blocco — e all'interno del blocco restituisci il valore con yield:
int score = 78;
String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> "B";
case 7 -> {
System.out.println("close to B");
yield "C";
}
case 6 -> "D";
default -> "F";
};yield è come return, ma per l'espressione switch — imposta il valore di questo switch ed esce dal case. (Non confonderlo con return, che uscirebbe dal metodo contenitore.)
Hai bisogno di yield solo all'interno di un blocco ({ ... }). Un case a espressione singola come case 8 -> "B"; restituisce già il suo valore direttamente, quindi aggiungere yield lì sarebbe un errore di compilazione.
yield è una parola chiave contestuale: agisce come parola chiave solo all'interno di un blocco switch. Il codice che usava yield come nome di variabile o metodo prima di Java 14 continua a compilarsi, quindi adottare le espressioni switch non rompe mai gli identificatori esistenti.
Le istruzioni funzionano ancora
Puoi usare la sintassi a freccia anche per gli effetti collaterali, nel qual caso è un'istruzione e non un'espressione:
switch (day) {
case "MON", "TUE", "WED", "THU", "FRI" -> System.out.println("weekday");
case "SAT", "SUN" -> System.out.println("weekend");
default -> System.out.println("unknown");
}Il vantaggio rispetto alla vecchia forma: nessun break, nessun bug da fall-through, più i case a più etichette visti sopra.
Esaustività
Quando un'espressione switch assegna a una variabile, il compilatore richiede che ogni possibile input produca un valore. Per un enum, ciò significa coprire tutte le costanti o fornire un default:
enum Status { PENDING, ACTIVE, DONE }
Status s = Status.ACTIVE;
String label = switch (s) {
case PENDING -> "waiting";
case ACTIVE -> "running";
case DONE -> "complete";
}; // no default needed — all enum values are coveredSe in seguito aggiungi una nuova costante all'enum e dimentichi di aggiornare lo switch, il compilatore te lo dirà. Questa è una forte garanzia di correttezza che non ottieni con if/else.
Pattern matching (Java 21+)
Java 21 ha reso il pattern matching per switch una funzionalità standard. Puoi fare lo switch sul tipo di un valore e associarlo a una variabile tipizzata all'interno del case:
Object o = 42;
String description = switch (o) {
case Integer i when i < 0 -> "negative int: " + i;
case Integer i -> "non-negative int: " + i;
case String s -> "string of length " + s.length();
case null -> "null value";
default -> "something else";
};La clausola when è una guardia — restringe ulteriormente un case con una condizione booleana. Questo sostituisce molte catene instanceof e si abbina naturalmente alle funzionalità più ampie di pattern matching.
Due regole da tenere a mente:
- Un
switchtradizionale rifiuta un selettorenullcon unaNullPointerException. Un pattern switch può includere un esplicitocase null— senza di esso, unnullgenera ancora un'eccezione. Non è più possibile ignorare silenziosamente unnullche raggiunge lo switch. - I case vengono testati dall'alto verso il basso, quindi ordina dal più specifico al più generale. Il
case Integer i when i < 0con guardia deve venire prima del semplicecase Integer i, altrimenti il case senza guardia ingoierebbe ogni intero per primo.
Esempio pratico
Cosa c'è dopo
Abbiamo trattato i condizionali. Ora arrivano i cicli: il ciclo while è il più semplice di tutti.