Alternativa a un’istruzione goto in Java

Qual è una funzione alternativa per la parola chiave goto in Java?

Poiché Java non ha un goto.

Potresti utilizzare un’istruzione BREAK con etichetta:

 search: for (i = 0; i < arrayOfInts.length; i++) { for (j = 0; j < arrayOfInts[i].length; j++) { if (arrayOfInts[i][j] == searchfor) { foundIt = true; break search; } } } 

Tuttavia, nel codice correttamente progettato, non è necessario disporre della funzionalità GOTO.

Non esiste alcun equivalente diretto al concetto goto in Java. Ci sono alcuni costrutti che ti permettono di fare alcune delle cose che puoi fare con un goto classico.

  • Le istruzioni break e continue ti consentono di saltare da un blocco in un ciclo o in un’istruzione switch.
  • Un’istruzione etichettata e break consentono di saltare da un’istruzione composta arbitraria a qualsiasi livello all’interno di un determinato metodo (o blocco di inizializzazione).
  • Se si etichetta un’istruzione loop, è ansible continue per continuare con la successiva iterazione di un ciclo esterno da un loop interno.
  • Lanciare e catturare le eccezioni ti permette di (efficacemente) saltare fuori da molti livelli di una chiamata di metodo. (Tuttavia, le eccezioni sono relativamente costose e sono considerate un cattivo modo per eseguire il stream di controllo “ordinario” 1 ).
  • E naturalmente, c’è un return .

Nessuno di questi costrutti Java consente di dirottare all’indietro o in un punto del codice allo stesso livello di nidificazione dell’istruzione corrente. Saltano tutti fuori uno o più livelli di nidificazione (scope) e tutti (a parte continue ) salgono verso il basso. Questa restrizione aiuta ad evitare la sindrome goto “spaghetti code” inerente al vecchio codice BASIC, FORTRAN e COBOL codice 2 .


1- La parte più costosa delle eccezioni è la creazione effettiva dell’object di eccezione e il suo stacktrace. Se davvero, è necessario utilizzare la gestione delle eccezioni per il controllo del stream “normale”, è ansible prealloca / riutilizzare l’object eccezione o creare una class di eccezioni personalizzata che sovrascrive il metodo fillInStackTrace() . Lo svantaggio è che i metodi printStackTrace() dell’eccezione non ti daranno informazioni utili … nel caso avessi bisogno di chiamarli.

2 – La sindrome del codice spaghetti ha generato l’approccio alla programmazione strutturata , in cui si è limitato l’utilizzo dei costrutti linguistici disponibili. Questo potrebbe essere applicato a BASIC , Fortran e COBOL , ma ha richiesto attenzione e disciplina. Liberarsi del goto interamente era una soluzione pragmaticamente migliore. Se lo tenga in una lingua, c’è sempre qualche pagliaccio che lo abuserà.

Solo per divertimento, ecco un’implementazione GOTO in Java.

Esempio:

  1 public class GotoDemo { 2 public static void main(String[] args) { 3 int i = 3; 4 System.out.println(i); 5 i = i - 1; 6 if (i >= 0) { 7 GotoFactory.getSharedInstance().getGoto().go(4); 8 } 9 10 try { 11 System.out.print("Hell"); 12 if (Math.random() > 0) throw new Exception(); 13 System.out.println("World!"); 14 } catch (Exception e) { 15 System.out.print("o "); 16 GotoFactory.getSharedInstance().getGoto().go(13); 17 } 18 } 19 } 

Eseguendolo:

 $ java -cp bin:asm-3.1.jar GotoClassLoader GotoDemo 3 2 1 0 Hello World! 

Devo aggiungere “non usarlo!”?

Mentre alcuni commentatori e downvoters sostengono che non si tratta di goto , il bytecode generato dalle istruzioni Java sottostanti suggerisce davvero che queste istruzioni esprimono davvero la semantica goto .

Nello specifico, il do {...} while(true); il ciclo nel secondo esempio è ottimizzato dai compilatori Java per non valutare la condizione del ciclo.

Saltando in avanti

 label: { // do stuff if (check) break label; // do more stuff } 

In bytecode:

 2 iload_1 [check] 3 ifeq 6 // Jumping forward 6 .. 

Saltando all’indietro

 label: do { // do stuff if (check) continue label; // do more stuff break label; } while(true); 

In bytecode:

  2 iload_1 [check] 3 ifeq 9 6 goto 2 // Jumping backward 9 .. 

Se vuoi veramente qualcosa come le istruzioni goto, puoi sempre provare a rompere i blocchi con nome.

Devi essere all’interno dell’ambito del blocco per rompere con l’etichetta:

 namedBlock: { if (j==2) { // this will take you to the label above break namedBlock; } } 

Non ti spiegherò perché dovresti evitare il goto – presumo che tu già conosca la risposta.

 public class TestLabel { enum Label{LABEL1, LABEL2, LABEL3, LABEL4} /** * @param args */ public static void main(String[] args) { Label label = Label.LABEL1; while(true) { switch(label){ case LABEL1: print(label); case LABEL2: print(label); label = Label.LABEL4; continue; case LABEL3: print(label); label = Label.LABEL1; break; case LABEL4: print(label); label = Label.LABEL3; continue; } break; } } public final static void print(Label label){ System.out.println(label); } 

StephenC scrive:

Ci sono due costrutti che ti permettono di fare alcune delle cose che puoi fare con un goto classico.

Ancora uno …

Matt Wolfe scrive:

La gente parla sempre di non usare mai un goto, ma penso che ci sia un buon caso d’uso del mondo reale che è abbastanza noto e usato … Ovvero, assicurandosi di eseguire del codice prima di un ritorno da una funzione .. Solitamente il suo rilascio serrature o cosa no, ma nel mio caso mi piacerebbe essere in grado di saltare a una pausa subito prima del ritorno in modo da poter effettuare una pulizia obbligatoria obbligatoria.

 try { // do stuff return result; // or break, etc. } finally { // clean up before actually returning, even though the order looks wrong. } 

http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html

Il blocco finally viene sempre eseguito quando si esce dal blocco try. Ciò garantisce che il blocco finally venga eseguito anche se si verifica un’eccezione imprevista. Ma alla fine è utile non solo per la gestione delle eccezioni, ma consente al programmatore di evitare che il codice di pulizia venga ignorato accidentalmente da un ritorno, una continuazione o un’interruzione. Mettere il codice di pulizia in un blocco finale è sempre una buona pratica, anche quando non sono previste eccezioni.

La domanda di intervista sciocca associata alla fine è: Se si ritorna da un blocco try {}, ma si ha anche un ritorno nel proprio {} finale, quale valore viene restituito?

Prova il codice qui sotto. Per me funziona.

 for (int iTaksa = 1; iTaksa < =8; iTaksa++) { // 'Count 8 Loop is 8 Taksa strTaksaStringStar[iCountTaksa] = strTaksaStringCount[iTaksa]; LabelEndTaksa_Exit : { if (iCountTaksa == 1) { //If count is 6 then next it's 2 iCountTaksa = 2; break LabelEndTaksa_Exit; } if (iCountTaksa == 2) { //If count is 2 then next it's 3 iCountTaksa = 3; break LabelEndTaksa_Exit; } if (iCountTaksa == 3) { //If count is 3 then next it's 4 iCountTaksa = 4; break LabelEndTaksa_Exit; } if (iCountTaksa == 4) { //If count is 4 then next it's 7 iCountTaksa = 7; break LabelEndTaksa_Exit; } if (iCountTaksa == 7) { //If count is 7 then next it's 5 iCountTaksa = 5; break LabelEndTaksa_Exit; } if (iCountTaksa == 5) { //If count is 5 then next it's 8 iCountTaksa = 8; break LabelEndTaksa_Exit; } if (iCountTaksa == 8) { //If count is 8 then next it's 6 iCountTaksa = 6; break LabelEndTaksa_Exit; } if (iCountTaksa == 6) { //If count is 6 then loop 1 as 1 2 3 4 7 5 8 6 --> 1 iCountTaksa = 1; break LabelEndTaksa_Exit; } } //LabelEndTaksa_Exit : { } // "for (int iTaksa = 1; iTaksa < =8; iTaksa++) {" 

Il più semplice è:

 int label = 0; loop:while(true) { switch(state) { case 0: // Some code state = 5; break; case 2: // Some code state = 4; break; ... default: break loop; } } 

Usa un’interruzione etichettata come alternativa al goto.

Java non ha goto , perché rende il codice non strutturato e non chiaro da leggere. Tuttavia, puoi usare break e continue come forma civilizzata di goto senza i suoi problemi.


Saltare in avanti usando la pausa –

 ahead: { System.out.println("Before break"); break ahead; System.out.println("After Break"); // This won't execute } // After a line break ahead, the code flow starts from here, after the ahead block System.out.println("After ahead"); 

Uscita :

 Before Break After ahead 

Saltare all’indietro usando continua

 before: { System.out.println("Continue"); continue before; } 

Ciò comporterà un ciclo infinito in quanto ogni volta che viene eseguita la linea continue before dell’esecuzione, il stream di codice ricomincerà da before .