@harigm: Können Sie ein Beispiel für die Art von Code geben, den Sie schreiben würden, wenn ergoto in Java verfügbar wäre ? Ich denke, darin liegt das wichtigere Thema.
Polygenelubricants
3
@harigm: Ich zweite Polygene, könnten Sie bitte Ihre Frage aktualisieren, um anzugeben, warum Ihr Programm ein goto benötigt?
Jeder
13
Die Leute reden immer davon, niemals ein goto zu verwenden, aber ich denke, es gibt einen wirklich guten realen Anwendungsfall, der ziemlich bekannt und verwendet ist. Das heißt, Sie müssen sicherstellen, dass Sie Code ausführen, bevor Sie von einer Funktion zurückkehren. Normalerweise wird er freigegeben Schlösser oder was nicht, aber in meinem Fall würde ich gerne kurz vor der Rückkehr zu einer Pause springen können, damit ich die erforderliche obligatorische Bereinigung durchführen kann. Natürlich wäre ein Programm, das überall mit Goto übersät ist, schrecklich, aber wenn es auf Methodenkörper beschränkt ist, scheint es nicht so schlimm zu sein, solange Sie einer Konvention folgen (nur bis zum Ende der Funktionen springen, niemals sichern)
Matt Wolfe
7
Du bist in guter Gesellschaft. Linus Torvalds hat goto kerneltrap.org/node/553 leidenschaftlich verteidigt . Zweifellos die bemerkenswerteste Auslassung von Project Coin.
Fletch
7
@MattWolfe versucht in diesem Beispiel nicht, den Job endlich zu erledigen?
Andres Riofrio
Antworten:
83
Sie können eine beschriftete BREAK- Anweisung verwenden:
Warum sollten Sie keine goto-Funktionalität für richtig gestalteten Code benötigen?
Rogermushroom
13
Weil nur die Baugruppe verwendet werden darf. Eine while-Anweisung, eine for-Anweisung, eine do-while-Anweisung, eine foreach-Anweisung, ein Funktionsaufruf - all dies sind GOTO-Anweisungen, die auf kontrollierte und vorhersehbare Weise verwendet werden. Auf der Ebene des Assemblers handelt es sich immer um GOTOs, aber Sie sollten nicht die Funktionalität benötigen, die Ihnen GOTO bietet. Der einzige Vorteil gegenüber Funktionen und Schleifen- / Steueranweisungen besteht darin, dass Sie überall in die Mitte eines Codes springen können . Und dieser "Vorteil" ist selbst die Definition von "Spaghetti-Code".
1
@whatsthebeef Hier ist der berühmte Brief von Edsger W. Dijkstra aus dem Jahr 1968, der erklärt, warum er ihn für schädlich hielt .
gotoIn Java gibt es kein direktes Äquivalent zum Konzept. Es gibt einige Konstrukte, mit denen Sie einige der Dinge tun können, die Sie mit einem Klassiker tun können goto.
Mit den Anweisungen breakund continuekönnen Sie in einer Schleifen- oder switch-Anweisung aus einem Block springen.
Eine beschriftete Anweisung break <label>, mit der Sie aus einer beliebigen zusammengesetzten Anweisung auf eine beliebige Ebene innerhalb einer bestimmten Methode (oder eines Initialisierungsblocks) springen können.
Wenn Sie eine Schleifenanweisung beschriften, können Sie continue <label>mit der nächsten Iteration einer äußeren Schleife von einer inneren Schleife fortfahren.
Durch das Auslösen und Abfangen von Ausnahmen können Sie (effektiv) aus vielen Ebenen eines Methodenaufrufs herausspringen. (Ausnahmen sind jedoch relativ teuer und werden als schlechter Weg zur Durchführung eines "normalen" Kontrollflusses angesehen. 1. )
Und natürlich gibt es return.
Mit keinem dieser Java-Konstrukte können Sie rückwärts oder zu einem Punkt im Code auf derselben Verschachtelungsebene wie die aktuelle Anweisung verzweigen. Sie alle springen aus einer oder mehreren Verschachtelungsebenen (Scope) und alle (abgesehen von continue) nach unten. Diese Einschränkung hilft, das Goto-Spaghetti-Code-Syndrom zu vermeiden, das dem alten BASIC-, FORTRAN- und COBOL-Code 2 innewohnt .
1- Der teuerste Teil von Ausnahmen ist die tatsächliche Erstellung des Ausnahmeobjekts und seiner Stapelverfolgung. Wenn Sie die Ausnahmebehandlung wirklich, wirklich für die "normale" Flusssteuerung verwenden müssen, können Sie das Ausnahmeobjekt entweder vorab zuordnen / wiederverwenden oder eine benutzerdefinierte Ausnahmeklasse erstellen, die die fillInStackTrace()Methode überschreibt . Der Nachteil ist, dass die printStackTrace()Methoden der Ausnahme keine nützlichen Informationen liefern ... falls Sie sie jemals aufrufen müssen.
2 - Das Spaghetti-Code-Syndrom hat den strukturierten Programmieransatz hervorgebracht , bei dem Sie die Verwendung der verfügbaren Sprachkonstrukte eingeschränkt haben. Dies könnte auf BASIC , Fortran und COBOL angewendet werden, erfordert jedoch Sorgfalt und Disziplin. gotoGanz loszuwerden war eine pragmatisch bessere Lösung. Wenn Sie es in einer Sprache halten, gibt es immer einen Clown, der es missbraucht.
Durch Schleifenkonstrukte wie while (...) {} und for (...) {} können Sie Codeblöcke wiederholen, ohne explizit an eine beliebige Stelle zu springen. Mit Methodenaufrufen von myMethod () können Sie außerdem Code ausführen, der an anderer Stelle gefunden wurde, und nach Abschluss zum aktuellen Block zurückkehren. All dies kann verwendet werden, um die Funktionalität von goto zu ersetzen und gleichzeitig das häufig auftretende Problem zu vermeiden, dass goto nicht zurückgegeben werden kann.
Chris Nava
@Chris - das stimmt, aber die meisten Sprachen, die GOTO unterstützen, haben auch engere Analoga zu Java-Schleifenkonstrukten.
Stephen C
1
@Chris - klassisches FORTRAN hat 'for'-Schleifen und klassisches COBOL hat auch Schleifenkonstrukte (obwohl ich mich nicht an die Details erinnern kann). Nur klassisches BASIC hat keine expliziten Schleifenkonstrukte ...
Stephen C
31
Nur zum Spaß, hier ist eine GOTO-Implementierung in Java.
Beispiel:
1publicclassGotoDemo{2publicstaticvoid main(String[] args){3int i =3;4System.out.println(i);5 i = i -1;6if(i >=0){7GotoFactory.getSharedInstance().getGoto().go(4);8}910try{11System.out.print("Hell");12if(Math.random()>0)thrownewException();13System.out.println("World!");14}catch(Exception e){15System.out.print("o ");16GotoFactory.getSharedInstance().getGoto().go(13);17}18}19}
Ja, schon. Alles, was nur durch Bytecode-Hacker implementiert werden kann, ist nicht wirklich Java ...
Stephen C
Gibt es etwas Ähnliches, das Beschriftungen anstelle von Zeilennummern unterstützt?
Behrouz.M
1
Die Verknüpfung zu einer Implementierung ist unterbrochen.
LarsH
17
Während einige Kommentatoren und Downvoter argumentieren, dass dies nicht goto ist , deutet der generierte Bytecode aus den folgenden Java-Anweisungen wirklich darauf hin, dass diese Anweisungen wirklich goto- Semantik ausdrücken .
Insbesondere wird die do {...} while(true);Schleife im zweiten Beispiel von Java-Compilern optimiert, um die Schleifenbedingung nicht zu bewerten.
Vorwärts springen
label:{// do stuffif(check)break label;// do more stuff}
Im Bytecode:
2 iload_1 [check]3 ifeq 6// Jumping forward6..
Rückwärts springen
label:do{// do stuffif(check)continue label;// do more stuffbreak label;}while(true);
Wie springt das do / while rückwärts? Es bricht immer noch aus dem Block aus. Zähler Beispiel: label: do {System.out.println ("Rückwärts"); Beschriftung fortsetzen;} while (false); Ich denke in Ihrem Beispiel ist die Weile (wahr) das, was für den Rückwärtssprung verantwortlich ist.
Ben Holland
@ BenHolland: continue labelist der Sprung zurück
Lukas Eder
Ich bin immer noch nicht überzeugt. Die continue-Anweisung überspringt die aktuelle Iteration einer for-, while- oder do-while-Schleife. Das Fortsetzen springt zum Ende des Schleifenkörpers, der dann auf den booleschen Ausdruck ausgewertet wird, der die Schleife steuert. Die Weile ist das, was rückwärts springt, nicht das Weiter. Siehe docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
Ben Holland
@ BenHolland: Technisch gesehen hast du recht. Aus logischer Sicht ", wenn // do stuffund // do more stuffsind die Aussagen von Interesse, continue label" hat den Effekt " , rückwärts zu springen (aufgrund der while(true)Aussage natürlich). Mir war jedoch nicht bewusst, dass dies eine so genaue Frage war ...
Lukas Eder
2
@ BenHolland: Ich habe die Antwort aktualisiert. Das while(true)wird vom Compiler in eine gotoBytecode-Operation übersetzt. Da truees sich um ein konstantes Literal handelt, kann der Compiler diese Optimierung durchführen und muss nichts auswerten. Also, mein Beispiel ist wirklich ein Goto, rückwärts springen ...
Lukas Eder
5
Wenn Sie wirklich so etwas wie goto-Anweisungen wollen, können Sie immer versuchen, in benannte Blöcke zu brechen.
Sie müssen sich im Rahmen des Blocks befinden, um zum Label zu gelangen:
namedBlock:{if(j==2){// this will take you to the label abovebreak namedBlock;}}
Ich werde Ihnen nicht erklären, warum Sie Goto vermeiden sollten - ich gehe davon aus, dass Sie die Antwort darauf bereits kennen.
Ich habe versucht, dies in meiner hier verlinkten SO-Frage zu implementieren . Es führt Sie tatsächlich nicht zum "Etikett oben", vielleicht habe ich die Aussagen des Autors falsch interpretiert, aber der Klarheit halber füge ich hinzu, dass Sie zum Ende des äußeren Blocks (des "beschrifteten" Blocks) gelangen, der sich unten befindet wo du brechst. Daher kann man nicht nach oben "brechen". Zumindest ist das mein Verständnis.
Dies führt zu einer StackOverFlowException, wenn Sie sie lange genug ausführen, also muss ich gehen.
Kevin Parker
3
@ Kevin: Wie führt dies zu einem Stapelüberlauf? Es gibt keine Rekursion in diesem Algorithmus ...
Lukas Eder
1
Dies ähnelt sehr dem ursprünglichen Beweis, dass jedes Programm ohne Verwendung von "goto" geschrieben werden kann - indem es in eine while-Schleife mit einer "label" -Variablen umgewandelt wird, die zehnmal schlechter ist als jedes goto.
Gnasher729
3
StephenC schreibt:
Es gibt zwei Konstrukte, mit denen Sie einige der Dinge tun können, die Sie mit einem klassischen goto tun können.
Einer noch...
Matt Wolfe schreibt:
Die Leute reden immer davon, niemals ein goto zu verwenden, aber ich denke, es gibt einen wirklich guten realen Anwendungsfall, der ziemlich bekannt und verwendet ist. Das heißt, Sie müssen sicherstellen, dass Sie Code ausführen, bevor Sie von einer Funktion zurückkehren. Normalerweise wird er freigegeben Schlösser oder was nicht, aber in meinem Fall würde ich gerne kurz vor der Rückkehr zu einer Pause springen können, damit ich die erforderliche obligatorische Bereinigung durchführen kann.
try{// do stuffreturn result;// or break, etc.}finally{// clean up before actually returning, even though the order looks wrong.}
Der finally-Block wird immer ausgeführt, wenn der try-Block beendet wird. Dadurch wird sichergestellt, dass der finally-Block auch dann ausgeführt wird, wenn eine unerwartete Ausnahme auftritt. Aber schließlich ist es nicht nur für die Ausnahmebehandlung nützlich - es ermöglicht dem Programmierer zu vermeiden, dass der Bereinigungscode versehentlich durch eine Rückgabe umgangen, fortgesetzt oder unterbrochen wird. Das Einfügen von Bereinigungscode in einen finally-Block ist immer eine gute Vorgehensweise, auch wenn keine Ausnahmen zu erwarten sind.
Die dumme Interviewfrage, die mit finally verbunden ist, lautet: Wenn Sie von einem try {} -Block zurückkehren, aber auch eine return in Ihrem finally {} haben, welcher Wert wird zurückgegeben?
Ich bin nicht der Meinung, dass dies eine dumme Interviewfrage ist. Ein erfahrener Java-Programmierer sollte wissen, was passiert, wenn er nur verstehen will , warum das Einfügen returneines finallyBlocks eine schlechte Idee ist. (Und selbst wenn das Wissen nicht unbedingt notwendig ist, würde ich mir Sorgen um einen Programmierer machen, der nicht die Neugier hatte, es herauszufinden ...)
Stephen C
1
Am einfachsten ist:
int label =0;
loop:while(true){switch(state){case0:// Some code
state =5;break;case2:// Some code
state =4;break;...default:break loop;}}
Versuchen Sie den folgenden Code. Für mich geht das.
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;breakLabelEndTaksa_Exit;}if(iCountTaksa ==2){//If count is 2 then next it's 3
iCountTaksa =3;breakLabelEndTaksa_Exit;}if(iCountTaksa ==3){//If count is 3 then next it's 4
iCountTaksa =4;breakLabelEndTaksa_Exit;}if(iCountTaksa ==4){//If count is 4 then next it's 7
iCountTaksa =7;breakLabelEndTaksa_Exit;}if(iCountTaksa ==7){//If count is 7 then next it's 5
iCountTaksa =5;breakLabelEndTaksa_Exit;}if(iCountTaksa ==5){//If count is 5 then next it's 8
iCountTaksa =8;breakLabelEndTaksa_Exit;}if(iCountTaksa ==8){//If count is 8 then next it's 6
iCountTaksa =6;breakLabelEndTaksa_Exit;}if(iCountTaksa ==6){//If count is 6 then loop 1 as 1 2 3 4 7 5 8 6 --> 1
iCountTaksa =1;breakLabelEndTaksa_Exit;}}//LabelEndTaksa_Exit : {}// "for (int iTaksa = 1; iTaksa <=8; iTaksa++) {"
Java hat keine goto, weil es den Code unstrukturiert und unklar zu lesen macht. Sie können jedoch breakund continueals zivilisierte Form von goto ohne seine Probleme verwenden.
Mit Pause nach vorne springen -
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 blockSystem.out.println("After ahead");
Ihr Beispiel für das Zurückspringen mit continue ist falsch. Sie können "continue" nicht außerhalb des Hauptteils einer Schleife verwenden, da dies einen Kompilierungsfehler verursacht. In einer Schleife springt jedoch weiter einfach die bedingte Schleife. So etwas wie while (true) {continue;} wäre eine Endlosschleife, aber so ist while (true) {}.
goto
in Java verfügbar wäre ? Ich denke, darin liegt das wichtigere Thema.Antworten:
Sie können eine beschriftete BREAK- Anweisung verwenden:
In richtig entworfenem Code sollten Sie jedoch keine GOTO-Funktionalität benötigen.
quelle
goto
In Java gibt es kein direktes Äquivalent zum Konzept. Es gibt einige Konstrukte, mit denen Sie einige der Dinge tun können, die Sie mit einem Klassiker tun könnengoto
.break
undcontinue
können Sie in einer Schleifen- oder switch-Anweisung aus einem Block springen.break <label>
, mit der Sie aus einer beliebigen zusammengesetzten Anweisung auf eine beliebige Ebene innerhalb einer bestimmten Methode (oder eines Initialisierungsblocks) springen können.continue <label>
mit der nächsten Iteration einer äußeren Schleife von einer inneren Schleife fortfahren.return
.Mit keinem dieser Java-Konstrukte können Sie rückwärts oder zu einem Punkt im Code auf derselben Verschachtelungsebene wie die aktuelle Anweisung verzweigen. Sie alle springen aus einer oder mehreren Verschachtelungsebenen (Scope) und alle (abgesehen von
continue
) nach unten. Diese Einschränkung hilft, das Goto-Spaghetti-Code-Syndrom zu vermeiden, das dem alten BASIC-, FORTRAN- und COBOL-Code 2 innewohnt .1- Der teuerste Teil von Ausnahmen ist die tatsächliche Erstellung des Ausnahmeobjekts und seiner Stapelverfolgung. Wenn Sie die Ausnahmebehandlung wirklich, wirklich für die "normale" Flusssteuerung verwenden müssen, können Sie das Ausnahmeobjekt entweder vorab zuordnen / wiederverwenden oder eine benutzerdefinierte Ausnahmeklasse erstellen, die die
fillInStackTrace()
Methode überschreibt . Der Nachteil ist, dass dieprintStackTrace()
Methoden der Ausnahme keine nützlichen Informationen liefern ... falls Sie sie jemals aufrufen müssen.2 - Das Spaghetti-Code-Syndrom hat den strukturierten Programmieransatz hervorgebracht , bei dem Sie die Verwendung der verfügbaren Sprachkonstrukte eingeschränkt haben. Dies könnte auf BASIC , Fortran und COBOL angewendet werden, erfordert jedoch Sorgfalt und Disziplin.
goto
Ganz loszuwerden war eine pragmatisch bessere Lösung. Wenn Sie es in einer Sprache halten, gibt es immer einen Clown, der es missbraucht.quelle
Nur zum Spaß, hier ist eine GOTO-Implementierung in Java.
Muss ich hinzufügen "benutze es nicht!"?
quelle
Math.random()
Während einige Kommentatoren und Downvoter argumentieren, dass dies nicht goto ist , deutet der generierte Bytecode aus den folgenden Java-Anweisungen wirklich darauf hin, dass diese Anweisungen wirklich goto- Semantik ausdrücken .
Insbesondere wird die
do {...} while(true);
Schleife im zweiten Beispiel von Java-Compilern optimiert, um die Schleifenbedingung nicht zu bewerten.Vorwärts springen
Im Bytecode:
Rückwärts springen
Im Bytecode:
quelle
continue label
ist der Sprung zurück// do stuff
und// do more stuff
sind die Aussagen von Interesse,continue label
" hat den Effekt " , rückwärts zu springen (aufgrund derwhile(true)
Aussage natürlich). Mir war jedoch nicht bewusst, dass dies eine so genaue Frage war ...while(true)
wird vom Compiler in einegoto
Bytecode-Operation übersetzt. Datrue
es sich um ein konstantes Literal handelt, kann der Compiler diese Optimierung durchführen und muss nichts auswerten. Also, mein Beispiel ist wirklich ein Goto, rückwärts springen ...Wenn Sie wirklich so etwas wie goto-Anweisungen wollen, können Sie immer versuchen, in benannte Blöcke zu brechen.
Sie müssen sich im Rahmen des Blocks befinden, um zum Label zu gelangen:
Ich werde Ihnen nicht erklären, warum Sie Goto vermeiden sollten - ich gehe davon aus, dass Sie die Antwort darauf bereits kennen.
quelle
quelle
StephenC schreibt:
Einer noch...
Matt Wolfe schreibt:
http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html
Die dumme Interviewfrage, die mit finally verbunden ist, lautet: Wenn Sie von einem try {} -Block zurückkehren, aber auch eine return in Ihrem finally {} haben, welcher Wert wird zurückgegeben?
quelle
return
einesfinally
Blocks eine schlechte Idee ist. (Und selbst wenn das Wissen nicht unbedingt notwendig ist, würde ich mir Sorgen um einen Programmierer machen, der nicht die Neugier hatte, es herauszufinden ...)Am einfachsten ist:
quelle
Versuchen Sie den folgenden Code. Für mich geht das.
quelle
Verwenden Sie eine beschriftete Pause als Alternative zu goto.
quelle
Java hat keine
goto
, weil es den Code unstrukturiert und unklar zu lesen macht. Sie können jedochbreak
undcontinue
als zivilisierte Form von goto ohne seine Probleme verwenden.Mit Pause nach vorne springen -
Ausgabe :
Rückwärts springen mit weiter
Dies führt zu einer Endlosschleife, da bei jeder
continue before
Ausführung der Zeile der Code-Fluss von neu gestartet wirdbefore
.quelle