Ich starre auf einen alten Code aus dem Jahr 2001 und bin auf diese Aussage gestoßen:
else {
do {
int c = XMLDocumentFragmentScannerImpl.this.scanContent();
if (c == 60) {
XMLDocumentFragmentScannerImpl.this.fEntityScanner.scanChar();
XMLDocumentFragmentScannerImpl.this.setScannerState(1);
break label913;
}
Ich hatte das noch nie gesehen und hier beschriftete Brüche entdeckt:
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
Funktioniert das nicht im Wesentlichen so goto
? Ist es überhaupt eine gute Praxis, es zu benutzen? Es macht mich unruhig.
label913
definiert?Antworten:
Nein, es ist nicht wie ein Goto, da Sie nicht zu einem anderen Teil des Kontrollflusses "gehen" können.
Von der Seite, die Sie verlinkt haben:
Dies bedeutet, dass Sie nur Schleifen unterbrechen können, die gerade ausgeführt werden.
Betrachten Sie dieses Beispiel:
first: for( int i = 0; i < 10; i++) { second: for(int j = 0; j < 5; j ++ ) { break xxx; } } third: for( int a = 0; a < 10; a++) { }
Sie könnten ersetzen
xxx
mitfirst
odersecond
(die äußere oder innere Schleife zu brechen), da beide Schleifen ausgeführt werden, wenn Sie die Hit -break
Anweisung, wobei jedochxxx
mitthird
nicht kompiliert.quelle
break first;
würde, würde der Compiler sofort wieder in die Zeile zurückkehrenfirst:
und diese Schleifen erneut ausführen?break first;
würde brechen (end) die Schleife markiertfirst
dh es würde weiterhin mitthird
. Auf der anderen Seitecontinue first;
würde die aktuelle Iteration von beendenfirst
und damit brechensecond
und mit dem nächsten Wert voni
in fortfahrenfirst
.Es ist nicht so schrecklich,
goto
weil es nur die Kontrolle an das Ende der beschrifteten Anweisung sendet (normalerweise ein Schleifenkonstrukt).goto
Was unwahrscheinlich macht, ist, dass es sich um einen beliebigen Zweig zu einem beliebigen Ort handelt, einschließlich Beschriftungen, die sich weiter oben in der Methodenquelle befinden, sodass Sie ein selbst entwickeltes Schleifenverhalten haben können. Die Label-Break-Funktionalität in Java lässt diese Art von Verrücktheit nicht zu, da die Steuerung nur vorwärts geht.Ich habe es vor ungefähr 12 Jahren nur einmal verwendet. In einer Situation, in der ich aus verschachtelten Schleifen ausbrechen musste, hätte die strukturiertere Alternative zu komplizierteren Tests in den Schleifen geführt. Ich würde nicht empfehlen, es häufig zu verwenden, aber ich würde es nicht als automatischen Geruch nach schlechtem Code kennzeichnen.
quelle
Es ist immer möglich, durch eine
break
neue Methode zu ersetzen .Betrachten Sie die Codeprüfung für alle gemeinsamen Elemente in zwei Listen:
List list1, list2; boolean foundCommonElement = false; for (Object el : list1) { if (list2.contains(el)) { foundCommonElement = true; break; } }
Sie können es folgendermaßen umschreiben:
boolean haveCommonElement(List list1, List list2) { for (Object el : list1) { if (list2.contains(el)) { return true; } } return false; }
Um nach gemeinsamen Elementen zwischen zwei Listen zu suchen, ist es natürlich besser, die
list1.retainAll(new HashSet<>(list2))
Methode zu verwenden, um diesO(n)
mitO(n)
zusätzlichem Speicher zu tun , oder beide Listen nach zu sortierenO(n * log n)
und dann nach gemeinsamen Elementen zu suchenO(n)
.quelle
goto
ist (potenziell) schädlich, weil es Ihnen so viele Orte schicken könnte - es ist überhaupt nicht ersichtlich, was erreicht wird; Das Erstellen einer Hilfsfunktion gibt dem nächsten Benutzer klare Grenzen, was Sie versuchen und wo Sie es tun werden, und gibt Ihnen die Möglichkeit, diese Funktionalität zu benennen. Viel klarer auf diese Weise.break
, nichtgoto
. Sicher, das hier gezeigte Refactoring ist in beiden Fällen eine gute Idee. Aber etikettiertbreak
ist bei weitem nicht so unmoderiert und neigt dazu, Code zu spaghettifizieren, wie esgoto
ist.Bevor Sie den Rest dieser Antwort lesen, lesen Sie bitte Gehe zu Erklärung, die als schädlich eingestuft wird . Wenn Sie es nicht vollständig lesen möchten, halte ich Folgendes für den entscheidenden Punkt:
Oder um es anders auszudrücken: Das Problem
goto
besteht darin, dass das Programm in der Mitte eines Codeblocks ankommen kann, ohne dass der Programmierer den Programmstatus zu diesem Zeitpunkt versteht. Die standardmäßigen blockorientierten Konstrukte sind so konzipiert, dass sie Zustandsübergänge klar abgrenzen. Ein Labelbreak
soll das Programm in einen bestimmten bekannten Zustand bringen (außerhalb des enthaltenden markierten Blocks).In einem imperativen Programm der realen Welt ist der Zustand nicht klar durch Blockgrenzen abgegrenzt, so dass es fraglich ist, ob das Etikett
break
eine gute Idee ist. Wenn der Block den von außerhalb des Blocks sichtbaren Status ändert und mehrere Punkte zum Verlassen des Blocks vorhanden sind, entspricht eine Beschriftungbreak
dem Grundelementgoto
. Der einzige Unterschied besteht darin, dass Sie anstelle der Chance, in der Mitte eines Blocks mit unbestimmtem Zustand zu landen, einen neuen Block mit unbestimmtem Zustand starten.Im Allgemeinen würde ich ein Etikett
break
als gefährlich betrachten. Meiner Meinung nach ist dies ein Zeichen dafür, dass der Block in eine Funktion mit eingeschränktem Zugriff auf den umschließenden Bereich konvertiert werden sollte.Dieser Beispielcode war jedoch eindeutig das Produkt eines Parser-Generators (das OP kommentierte, dass es sich um Xerces-Quellcode handelte). Und Parser-Generatoren (oder Code-Generatoren im Allgemeinen) nehmen sich oft Freiheiten mit dem Code, den sie generieren, weil sie den Zustand perfekt kennen und der Mensch ihn nicht verstehen muss.
quelle
break
ist bei mehreren Gelegenheiten sehr nützlich. Zum Beispiel, wenn Sie nach einem Wert suchen, der aus mehreren geordneten Quellen stammen kann. Sie versuchen es nacheinander, und wenn der erste gefunden wird, Siebreak
. Es ist eleganter Code alsif / else if / else if / else if
. (Zumindest sind es weniger Zeilen.) Es ist möglicherweise nicht immer praktisch, alles in einen Kontext einer Methode zu verschieben. Ein anderer Fall ist, wenn Sie bedingt von wenigen verschachtelten Ebenen abbrechen. Kurz gesagt, wenn Sie nicht mögenbreak
, dann mögen Siereturn
nirgendwo anders als am Ende einer Methode.do
undwhile
nicht existierten und jeder stattdessenif(.. ) goto
überall benutzte . Es sollte Sprachdesigner ermutigen, Unterstützung für Dinge wiedo
und hinzuzufügenwhile
. Als der Zug anfing zu rollen, entwickelte er sich leider zu einem Hype-Zug, der von unwissenden Leuten angetrieben wurde, die Dinge wiebreak
"nur einmal ausgeführt" -Schleifen steckten und Ausnahmen für diejenigengoto
machten , die wissen, was, für alle (seltenen) Fälle, die sauberer und weniger schädlich sind.Dies ist nicht wie eine
goto
Aussage, bei der Sie die Flusskontrolle rückwärts springen. Ein Etikett zeigt Ihnen (dem Programmierer) lediglich, wo die Unterbrechung aufgetreten ist. Außerdem wird die Flusskontrolle auf die nächste Anweisung nach dem übertragenbreak
.Ich persönlich denke, dass es nicht von großem Nutzen ist, wenn Sie Code schreiben, der Tausende von Zeilen wert ist, wird er unbedeutend. Aber auch hier würde es vom Anwendungsfall abhängen.
quelle
for(int i = 0; i < 1; i++) { do_something(); if(I_want_to_go_backwards) { i = 0; continue; } }
.Eine sauberere Art, die Absicht auszudrücken, könnte zumindest meiner Meinung nach darin bestehen, das Codefragment, das die Schleife enthält, in eine separate Methode zu setzen und einfach
return
daraus.Ändern Sie dies beispielsweise:
someLabel: for (int j = 0; j < 5; j++) { // do something if ... break someLabel; }
das mögen:
private void Foo() { for (int j = 0; j < 5; j++) { // do something if ... return; } }
Dies ist auch idiomatischer für Entwickler, die andere Sprachen beherrschen und möglicherweise in Zukunft (oder in Zukunft mit Ihrem Code ) arbeiten.
quelle