Я смотрю на какой-то старый код 2001 года и наткнулся на это утверждение:
else {
do {
int c = XMLDocumentFragmentScannerImpl.this.scanContent();
if (c == 60) {
XMLDocumentFragmentScannerImpl.this.fEntityScanner.scanChar();
XMLDocumentFragmentScannerImpl.this.setScannerState(1);
break label913;
}
Я никогда раньше такого не видел и обнаружил здесь помеченные разрывы:
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
Разве это не работает по существу goto
? Это вообще хорошая практика? Меня это беспокоит.
label913
определяется?Ответы:
Нет, это не похоже на goto, поскольку вы не можете «перейти» к другой части потока управления.
Со страницы, на которую вы указали:
Это означает, что вы можете прерывать только те циклы, которые выполняются в данный момент.
Рассмотрим этот пример:
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++) { }
Вы можете заменить
xxx
наfirst
илиsecond
(чтобы разорвать внешний или внутренний цикл), так как оба цикла выполняются, когда вы нажимаетеbreak
оператор, но заменаxxx
наthird
не будет компилироваться.источник
break first;
, компилятор вернулся бы к строке сразу послеfirst:
и снова выполнил бы эти циклы?break first;
не будет нарушать (конец) петля маркированыfirst
, то есть он будет продолжатьthird
. С другой стороны ,continue first;
положит конец текущей итерацииfirst
и разорватьsecond
вместе с этим и будет продолжать следующее значениеi
вfirst
.goto
в более или менее похожем смысле наif-else
утверждение. Goto гораздо более «гибкие», как в «идти куда угодно», например, в позицию перед циклом (на самом деле, как старые языки использовались для реализации циклов: «если условие не выполнено, перейти к началу тела цикла (снова)») . Недавно мне приходилось иметь с ними дело, и поверьте мне:goto
это настоящая заноза в заднице, а маркированные перерывы - нет.goto
вообще, даже если C ++ это поддерживает. Почти всегда есть лучший способ решить проблемы, который впоследствии избавит вас от головной боли. Может возникнуть искушение ... сопротивляйтесь!Это не так ужасно,
goto
потому что он отправляет управление только в конец помеченного оператора (обычно это конструкция цикла). Дело в том, что делаетgoto
несимпатичным, что это произвольная ветвь в любое место, в том числе этикетки нашла выше в источнике метода, так что вы можете иметь доморощенное зацикливание поведения. Функциональность разрыва ярлыков в Java не допускает такого безумия, потому что контроль только продвигается вперед.Я использовал его только один раз, около 12 лет назад, в ситуации, когда мне нужно было вырваться из вложенных циклов, более структурированная альтернатива могла бы сделать более сложные тесты в циклах. Я бы не советовал использовать его часто, но я бы не стал отмечать это как автоматический запах плохого кода.
источник
Всегда можно заменить
break
на новый метод.Рассмотрим проверку кода на наличие общих элементов в двух списках:
List list1, list2; boolean foundCommonElement = false; for (Object el : list1) { if (list2.contains(el)) { foundCommonElement = true; break; } }
Вы можете переписать это так:
boolean haveCommonElement(List list1, List list2) { for (Object el : list1) { if (list2.contains(el)) { return true; } } return false; }
Конечно, чтобы проверить общие элементы между двумя списками, лучше использовать
list1.retainAll(new HashSet<>(list2))
методO(n)
сO(n)
дополнительной памятью или отсортировать оба списка по адресу,O(n * log n)
а затем найти общие элементы по адресуO(n)
.источник
goto
(потенциально) вредно, потому что может отправить вас в очень много мест - совсем не очевидно, что делается; создание вспомогательной функции дает четкие границы следующему человеку в отношении того, что вы пытаетесь сделать и где вы собираетесь это делать, и дает вам возможность назвать эту часть функциональности. Этот способ более понятен.break
, а не о нейgoto
. Конечно, показанный здесь рефакторинг - хорошая идея в обоих случаях. Ноbreak
Labeled не так уж немодерирован и склонен к спагеттификации кода, какgoto
есть.Прежде чем читать оставшуюся часть этого ответа, прочтите « Перейти к заявлению, которое считается вредным» . Если вы не хотите читать его полностью, вот что я считаю ключевым:
Или, перефразируя, проблема
goto
в том, что программа может прибыть в середину блока кода, при этом программист не понимает состояние программы в этот момент. Стандартные блочно-ориентированные конструкции предназначены для четкого разграничения переходов между состояниями, обозначенногоbreak
предназначен для перевода программы в определенное известное состояние (за пределами содержащего помеченный блок).В реальной императивной программе состояние четко не очерчено границами блоков, поэтому сомнительно, что помеченное
break
- хорошая идея. Если блок меняет состояние, видимое снаружи блока, и есть несколько точек для выхода из блока, то помеченныйbreak
эквивалентен примитивуgoto
. Единственное отличие состоит в том, что вместо того, чтобы попасть в середину блока с неопределенным состоянием, вы начинаете новый блок с неопределенным состоянием.В общем, я считаю, что это
break
опасно. На мой взгляд, это признак того, что блок следует преобразовать в функцию с ограниченным доступом к охватывающей области.Однако этот пример кода явно был продуктом генератора синтаксического анализатора (OP прокомментировал, что это исходный код Xerces). А генераторы парсеров (или генераторы кода в целом) часто позволяют себе вольность с кодом, который они генерируют, потому что они прекрасно знают состояние, и людям не нужно его понимать.
источник
break
бывает весьма полезно в нескольких случаях. Например, когда вы ищете какую-то ценность, которая может поступать из нескольких упорядоченных источников. Вы пытаетесь один за другим, и когда первый найден, выbreak
. Это более элегантный код, чемif / else if / else if / else if
. (По крайней мере, меньше строк.) Перенос всего в контекст метода не всегда может быть практичным. Другой случай - если вы условно нарушаете несколько вложенных уровней. Короче говоря, если вам не нравитсяbreak
, то вам не нравитсяreturn
больше ничего, кроме конца метода.do
иwhile
не существует , и все использовалиif(.. ) goto
везде вместо этого. Он был призван побудить разработчиков языков добавить поддержку таких вещей, какdo
иwhile
. К сожалению, после того, как подножка начала катиться, он превратился в шумиху, подпитываемую невежественными людьми, помещающими такие вещиbreak
в циклы «выполняется только один раз» и выбрасывая исключения неизвестно для чего, для всех (редких) случаев, которыеgoto
являются более чистыми и менее вредными.Это не похоже на
goto
утверждение, в котором вы перемещаете управление потоком назад. Ярлык просто показывает вам (программисту), где произошел разрыв. Кроме того, управление потоком передается следующему оператору послеbreak
.Что касается его использования, я лично считаю, что он не имеет большого смысла, поскольку как только вы начинаете писать код, состоящий из тысяч строк, он становится несущественным. Но опять же, это будет зависеть от варианта использования.
источник
for(int i = 0; i < 1; i++) { do_something(); if(I_want_to_go_backwards) { i = 0; continue; } }
.Более ясным способом выражения намерения могло бы быть, по крайней мере, на мой взгляд, поместить фрагмент кода, содержащий цикл, в отдельный метод и просто
return
из него.Например, измените это:
someLabel: for (int j = 0; j < 5; j++) { // do something if ... break someLabel; }
в это:
private void Foo() { for (int j = 0; j < 5; j++) { // do something if ... return; } }
Это также более идиоматично для разработчиков, владеющих другими языками, которые могут работать с вашим кодом в будущем (или в будущем с вами ).
источник