Подтвердить что ты не робот

Является ли использование помеченного перерыва хорошей практикой в ​​Java?

Я смотрю на какой-то старый код 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? Это даже хорошая практика, чтобы использовать его? Это делает меня неловко

4b9b3361

Ответ 1

Нет, это не похоже на goto, поскольку вы не можете "перейти" к другой части потока управления.

На странице, которую вы указали:

Оператор break завершает обозначение с надписью; он не передает поток управления этикетке. Управляющий поток переносится в инструкцию сразу же после указанного (завершенного) оператора.

Это означает, что вы можете только разбить циклы, которые в настоящее время выполняются.

Рассмотрим следующий пример:

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 не будет компилироваться.

Ответ 2

Это не так страшно, как goto, потому что он только отправляет управление в конец помеченного оператора (как правило, контур цикла). То, что делает goto непохожим на то, что это произвольная ветка в любом месте, включая метки, найденные выше в источнике метода, так что вы можете иметь домашнее поведение цикла. Функциональность разрыва метки в Java не допускает такого безумия, потому что управление только продвигается вперед.

Я использовал его только 12 лет назад, в ситуации, когда мне нужно было вырваться из вложенных циклов, более структурированная альтернатива сделала бы более сложные тесты в циклах. Я бы не советовал использовать его много, но я бы не назвал его автоматическим запахом плохого кода.

Ответ 3

Прежде чем читать остальную часть этого ответа, пожалуйста, прочитайте Перейти к заявлению считается вредоносным. Если вы не хотите читать его полностью, вот что я считаю ключевым моментом:

Необузданное использование инструкции go to немедленно приводит к тому, что становится очень трудно найти значимый набор координат, в котором описывается прогресс процесса.

Или, чтобы перефразировать, проблема с goto заключается в том, что программа может прибыть в середине блока кода, без того, чтобы программист понимал состояние программы в этой точке. Стандартные блок-ориентированные конструкции предназначены для четкого определения переходов состояний, помеченный break предназначен для передачи программы в определенное известное состояние (вне содержащего помеченный блок).

В реальной императивной программе состояние четко не определено границами блоков, поэтому сомнительно, является ли помеченная break хорошей идеей. Если блок изменяет состояние, которое видно извне блока, и есть несколько точек для выхода из блока, то помеченная break эквивалентна примитиву goto. Единственное отличие состоит в том, что вместо того, чтобы попасть в середину блока с неопределенным состоянием, вы запускаете новый блок с неопределенным состоянием.

Итак, в общем, я считаю опасным помеченный break. По-моему, это знак того, что блок должен быть преобразован в функцию с ограниченным доступом к охватывающей области.

Однако этот примерный код был явно продуктом генератора синтаксического анализатора (OP прокомментировал, что это исходный код Xerces). И генераторы парсеров (или генераторы кода в целом) часто принимают вольности с кодом, который они генерируют, потому что у них есть совершенное знание о состоянии, и люди не должны его понимать.

Ответ 4

Всегда можно заменить 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).

Ответ 5

Это не похоже на инструкцию goto, в которой вы переводите управление потоком назад. Метка просто показывает вам (программисту), где произошел разрыв. Кроме того, управление потоком переносится в следующий оператор после break.

Об использовании этого я лично считаю, что он не имеет большого значения, поскольку, как только вы начинаете писать код на тысячи строк, он становится несущественным. Но опять же, это будет зависеть от варианта использования.

Ответ 6

Более чистым способом выражения намерения может быть, по крайней мере, на мой взгляд, поместить фрагмент кода, содержащий цикл, в отдельный метод и просто 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;
    }
}

Это также более идиоматично для разработчиков, владеющих другими языками, которые могут работать с вашим кодом в будущем (или в будущем вас).