Estoy mirando un código antiguo de 2001 y encontré esta declaración:
else {
do {
int c = XMLDocumentFragmentScannerImpl.this.scanContent();
if (c == 60) {
XMLDocumentFragmentScannerImpl.this.fEntityScanner.scanChar();
XMLDocumentFragmentScannerImpl.this.setScannerState(1);
break label913;
}
Nunca había visto esto antes y descubrí roturas etiquetadas aquí:
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
¿No funciona esto esencialmente goto
? ¿Es incluso una buena práctica usarlo? Me hace sentir incómodo.
label913
definido?Respuestas:
No, no es como un goto, ya que no puede "ir" a otra parte del flujo de control.
Desde la página que vinculó:
Esto significa que solo puede romper los bucles que se estén ejecutando actualmente.
Considere este ejemplo:
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++) { }
Puede reemplazar
xxx
confirst
osecond
(para romper el bucle externo o interno), ya que ambos bucles se están ejecutando, cuando presiona labreak
instrucción, pero reemplazarxxx
conthird
no se compilará.fuente
break first;
, si lo usara , ¿el compilador volvería a subir a la línea inmediatamente despuésfirst:
y ejecutaría esos bucles nuevamente?break first;
sería romper (fin) el bucle de etiquetadofirst
, es decir, que continuará conthird
. Por otro ladocontinue first;
, finalizaría la iteración actual defirst
y se romperíasecond
con eso y continuaría con el siguiente valor dei
infirst
.goto
más o menos en el mismo sentido que unaif-else
declaración. Los Goto son mucho más "flexibles" como en "ir a cualquier parte", por ejemplo, a una posición antes del bucle (de hecho, así es como los lenguajes antiguos solían implementar los bucles: "si no se cumple la condición, ve al inicio del cuerpo del bucle (de nuevo)") . He tenido que lidiar con ellos recientemente y créanme:goto
es un verdadero dolor de cabeza, mientras que las roturas etiquetadas no lo son.goto
en absoluto, incluso si C ++ lo admite. Casi siempre hay una mejor manera de resolver las cosas que te ahorrarán muchos dolores de cabeza más adelante. La tentación puede llegar ... ¡resiste!No es tan terrible
goto
porque solo envía el control al final de la declaración etiquetada (generalmente una construcción de bucle). Lo que lo hacegoto
desagradable es que es una rama arbitraria a cualquier lugar, incluidas las etiquetas que se encuentran más arriba en la fuente del método, de modo que puede tener un comportamiento de bucle propio. La funcionalidad de rotura de etiquetas en Java no permite ese tipo de locura porque el control solo avanza.Solo lo usé una vez hace unos 12 años, en una situación en la que necesitaba romper con los bucles anidados, la alternativa más estructurada habría hecho pruebas más complicadas en los bucles. No recomendaría usarlo mucho, pero no lo marcaría como un olor automático a código incorrecto.
fuente
Siempre es posible reemplazarlo
break
con un nuevo método.Considere la verificación de código para cualquier elemento común en dos listas:
List list1, list2; boolean foundCommonElement = false; for (Object el : list1) { if (list2.contains(el)) { foundCommonElement = true; break; } }
Puedes reescribirlo así:
boolean haveCommonElement(List list1, List list2) { for (Object el : list1) { if (list2.contains(el)) { return true; } } return false; }
Por supuesto, para buscar elementos comunes entre dos listas, es mejor usar el
list1.retainAll(new HashSet<>(list2))
método para hacerloO(n)
conO(n)
memoria adicional, o ordenar ambas listas enO(n * log n)
y luego encontrar elementos comunes enO(n)
.fuente
goto
es (potencialmente) dañino porque podría enviarle a muchos lugares; no es en absoluto evidente lo que se está logrando; La creación de una función de ayuda le da límites claros al siguiente tipo en cuanto a lo que está tratando de hacer y dónde lo va a hacer, y le da la oportunidad de nombrar esa parte de la funcionalidad. Mucho más claro de esta manera.break
, nogoto
. Claro, la refactorización que se muestra aquí es una buena idea en ambos casos. Pero etiquetadobreak
no es tan poco moderado y propenso al código espaguetizado comogoto
es.Antes de leer el resto de esta respuesta, lea Ir a la declaración considerada dañina . Si no quiere leerlo en su totalidad, aquí está lo que considero el punto clave:
O para reformular, el problema con
goto
es que el programa puede llegar en medio de un bloque de código, sin que el programador comprenda el estado del programa en ese punto. Las construcciones estándar orientadas a bloques están diseñadas para delinear claramente las transiciones de estado, un etiquetadobreak
está destinado a llevar el programa a un estado conocido específico (fuera del bloque etiquetado que lo contiene).En un programa imperativo del mundo real, el estado no está claramente delimitado por límites de bloque, por lo que es cuestionable si la etiqueta
break
es una buena idea. Si el bloque cambia de estado que es visible desde fuera del bloque, y hay varios puntos para salir del bloque, entonces una etiquetabreak
es equivalente a la primitivagoto
. La única diferencia es que, en lugar de la posibilidad de aterrizar en medio de un bloque con un estado indeterminado, comienzas un nuevo bloque con un estado indeterminado.Entonces, en general, consideraría una etiqueta
break
como peligrosa. En mi opinión, es una señal de que el bloque debe convertirse en una función, con acceso limitado al alcance adjunto.Sin embargo, este código de ejemplo fue claramente el producto de un generador de analizador (el OP comentó que era el código fuente de Xerces). Y los generadores de analizadores sintácticos (o generadores de código en general) a menudo se toman libertades con el código que generan, porque tienen un conocimiento perfecto del estado y los humanos no necesitan entenderlo.
fuente
break
es bastante útil en varias ocasiones. Por ejemplo, cuando busca algún valor que puede provenir de varias fuentes ordenadas. Estás probando uno por uno, y cuando se encuentra el primero, túbreak
. Es un código más elegante queif / else if / else if / else if
. (Al menos son menos líneas). Mover todo al contexto de un método puede no ser siempre práctico. Otro caso es, si está rompiendo condicionalmente desde unos pocos niveles anidados. En resumen, si no te gustabreak
, no te gustareturn
ningún otro lugar excepto el final de un método.do
ywhile
no existían y todos usaban enif(.. ) goto
todas partes. Tenía la intención de alentar a los diseñadores de idiomas a agregar soporte para cosas comodo
ywhile
. Lamentablemente, una vez que el tren comenzó a rodar, se convirtió en un tren de bombo publicitario impulsado por personas ignorantes que pusieron cosas comobreak
bucles de "solo se ejecuta una vez" y lanzaron excepciones a quién sabe qué, para todos los casos (raros) quegoto
es más limpio y menos dañino.Esto no es como una
goto
declaración en la que se hace retroceder el control de flujo. Una etiqueta simplemente le muestra a usted (el programador) dónde ha ocurrido la ruptura. Además, el control de flujo se transfiere a la siguiente declaración después debreak
.Sobre su uso, personalmente creo que no es de gran utilidad, ya que una vez que comienzas a escribir un código que vale miles de líneas, se vuelve insignificante. Pero de nuevo, dependería del caso de uso.
fuente
for(int i = 0; i < 1; i++) { do_something(); if(I_want_to_go_backwards) { i = 0; continue; } }
.Una forma más limpia de expresar la intención podría ser, al menos en mi opinión, poner el fragmento de código que contiene el bucle en un método separado y simplemente
return
partir de él.Por ejemplo, cambie esto:
someLabel: for (int j = 0; j < 5; j++) { // do something if ... break someLabel; }
dentro de esto:
private void Foo() { for (int j = 0; j < 5; j++) { // do something if ... return; } }
Esto también es más idiomático para los desarrolladores que dominan otros lenguajes que podrían funcionar con su código en el futuro (o en el futuro ).
fuente