Istruzioni con etichetta in Java
Usa le etichette in Java per uscire o continuare i cicli esterni dall'interno di cicli annidati.
Un semplice break esce dal ciclo più interno e un semplice continue salta un'iterazione di esso. Quando ti trovi all'interno di cicli annidati e hai bisogno di controllare il ciclo esterno da quello interno, Java ti offre le istruzioni con etichetta: un nome che puoi associare a un ciclo, e un break o continue che lo raggiunge tramite quel nome.
Questo capitolo tratta come definire un'etichetta, come break e continue con etichetta modificano il flusso, quando (raramente) usarli e le alternative più pulite da preferire.
Questa è la cosa più simile al goto che Java abbia — ed è intenzionalmente limitata ai cicli (e a switch).
Definire un'etichetta
Un'etichetta è un identificatore seguito da due punti, posizionato immediatamente prima di un ciclo:
outer:
for (int i = 0; i < 5; i++) {
// ...
}Il nome (outer in questo caso) segue le stesse regole di qualsiasi identificatore Java. Non è obbligatorio chiamarlo outer — searchLoop, rows, qualsiasi nome valido va bene. Scegli un nome che dica perché l'etichetta è lì.
break con etichetta
break <label>; esce dal ciclo contrassegnato con quell'etichetta, indipendentemente da quanto in profondità ti trovi all'interno:
int[][] grid = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int target = 5;
int foundRow = -1, foundCol = -1;
search:
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
if (grid[r][c] == target) {
foundRow = r;
foundCol = c;
break search;
}
}
}
System.out.println("found at " + foundRow + "," + foundCol);Senza il break con etichetta, avresti bisogno di una variabile flag o di ristrutturare il codice. Con esso, esci da entrambi i cicli contemporaneamente non appena viene trovato il valore cercato.
continue con etichetta
continue <label>; salta il resto sia dell'iterazione interna corrente sia di quella del ciclo esterno con etichetta, passando all'iterazione successiva di quest'ultimo:
rowLoop:
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
if (grid[r][c] < 0) {
continue rowLoop; // skip the rest of this row
}
process(grid[r][c]);
}
}Quando il ciclo interno incontra un valore negativo, il resto di quella riga viene saltato e si passa alla riga successiva.
Usarli con parsimonia
Le etichette funzionano, ma è facile abusarne. Ogni volta che stai per usarne una, considera prima le alternative:
-
Estrai un metodo di supporto. All'interno di un metodo, un semplice
returnesce da tutti i cicli contemporaneamente ed è di solito più chiaro:static int[] find(int[][] grid, int target) { for (int r = 0; r < grid.length; r++) { for (int c = 0; c < grid[r].length; c++) { if (grid[r][c] == target) return new int[]{r, c}; } } return null; } -
Usa un flag — leggermente meno pulito ma esplicito e senza
goto:boolean found = false; for (int r = 0; !found && r < grid.length; r++) { for (int c = 0; c < grid[r].length; c++) { if (grid[r][c] == target) { found = true; break; } } } -
Usa gli stream per problemi di filtraggio invece dei cicli annidati.
Ricorri alle etichette quando le alternative peggiorano davvero la leggibilità — tipicamente con due o tre livelli di profondità e con un'intenzione chiara di "interrompi la ricerca" o "prossima iterazione esterna".
Etichette e switch
Le etichette funzionano anche con switch, sebbene sia raro:
sw:
switch (cmd) {
case "x":
if (someCondition) break sw;
// ...
break;
}In pratica questo non aggiunge mai chiarezza rispetto a un semplice break.
Un esempio completo
Cosa c'è dopo
Hai completato i capitoli sul flusso di controllo: condizionali, operatore ternario, switch, cicli e le istruzioni che li interrompono. Il prossimo argomento riguarda i metodi — come raggruppare blocchi di codice in modo da poterli chiamare per nome.