Альтернатива выражению goto в Java - PullRequest
83 голосов
/ 12 марта 2010

Какая альтернативная функция для ключевого слова goto в Java?

Поскольку в Java нет перехода.

Ответы [ 11 ]

82 голосов
/ 12 марта 2010

Вы можете использовать помеченный оператор BREAK :

search:
    for (i = 0; i < arrayOfInts.length; i++) {
        for (j = 0; j < arrayOfInts[i].length; j++) {
            if (arrayOfInts[i][j] == searchfor) {
                foundIt = true;
                break search;
            }
        }
    }

Однако в правильно сконструированном коде вам не нужна функциональность GOTO.

41 голосов
/ 12 марта 2010

В Java нет прямого эквивалента концепции goto. Есть несколько конструкций, которые позволяют вам делать некоторые вещей, которые вы можете делать с классическим goto.

  • Операторы break и continue позволяют вам выпрыгнуть из блока в операторе цикла или переключателя.
  • Помеченный оператор и break <label> позволяют вам выпрыгнуть из произвольного составного оператора на любой уровень в рамках данного метода (или блока инициализатора).
  • Если вы пометите оператор цикла, вы можете continue <label> продолжить следующую итерацию внешнего цикла из внутреннего цикла.
  • Бросок и перехват исключений позволяет вам (эффективно) выпрыгивать из многих уровней вызова метода. (Однако исключения относительно дороги и считаются плохим способом сделать «обычный» поток управления 1 .)
  • И, конечно, есть return.

Ни одна из этих конструкций Java не позволяет вам переходить назад или к точке в коде на том же уровне вложенности, что и текущий оператор. Все они выпрыгивают на один или несколько уровней вложенности (объема), и все они (кроме continue) прыгают вниз. Это ограничение помогает избежать синдрома goto "код спагетти", присущего старому коду BASIC, FORTRAN и COBOL 2 .


1- Самая дорогая часть исключений - это фактическое создание объекта исключения и его трассировки стека. Если вам действительно нужно использовать обработку исключений для «нормального» управления потоком, вы можете либо предварительно выделить / повторно использовать объект исключения, либо создать собственный класс исключений, который переопределяет метод fillInStackTrace(). Недостатком является то, что методы printStackTrace() исключения не дадут вам полезной информации ... если вам когда-нибудь понадобится вызвать их.

2 - Синдром спагетти-кода породил подход структурированного программирования , когда вы ограничивали использование доступных языковых конструкций. Это может быть применено к BASIC , Fortran и COBOL , но это требует осторожности и дисциплины. Полностью избавиться от goto было прагматически лучшим решением. Если вы держите это на языке, всегда найдется клоун, который будет злоупотреблять этим.

29 голосов
/ 12 марта 2010

Ради интереса здесь - это реализация GOTO на Java.

Пример:

   1 public class GotoDemo {
   2     public static void main(String[] args) {
   3         int i = 3;
   4         System.out.println(i);
   5         i = i - 1;
   6         if (i >= 0) {
   7             GotoFactory.getSharedInstance().getGoto().go(4);
   8         }
   9         
  10         try {
  11             System.out.print("Hell");
  12             if (Math.random() > 0) throw new Exception();            
  13             System.out.println("World!");
  14         } catch (Exception e) {
  15             System.out.print("o ");
  16             GotoFactory.getSharedInstance().getGoto().go(13);
  17         }
  18     }
  19 }

Запуск:

$ java -cp bin:asm-3.1.jar GotoClassLoader GotoDemo           
   3
   2
   1
   0
   Hello World!

Нужно ли добавить «не используйте его!»?

17 голосов
/ 16 июня 2011

Хотя некоторые комментаторы и downvoters утверждают, что это не goto , сгенерированный байт-код из приведенных ниже Java-операторов действительно предполагает, что эти операторы действительно выражают семантику goto .

В частности, цикл do {...} while(true); во втором примере оптимизирован компиляторами Java, чтобы не оценивать условие цикла.

Прыжки вперед

label: {
  // do stuff
  if (check) break label;
  // do more stuff
}

В байт-коде:

2  iload_1 [check]
3  ifeq 6          // Jumping forward
6  ..

Прыжки назад

label: do {
  // do stuff
  if (check) continue label;
  // do more stuff
  break label;
} while(true);

В байт-коде:

 2  iload_1 [check]
 3  ifeq 9
 6  goto 2          // Jumping backward
 9  ..
5 голосов
/ 12 марта 2010

Если вы действительно хотите что-то вроде операторов goto, вы всегда можете попробовать разбить на именованные блоки.

Вы должны находиться в пределах блока, чтобы перейти к метке:

namedBlock: {
  if (j==2) {
    // this will take you to the label above
    break namedBlock;
  }
}

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

4 голосов
/ 04 марта 2011
public class TestLabel {

    enum Label{LABEL1, LABEL2, LABEL3, LABEL4}

    /**
     * @param args
     */
    public static void main(String[] args) {

        Label label = Label.LABEL1;

        while(true) {
            switch(label){
                case LABEL1:
                    print(label);

                case LABEL2:
                    print(label);
                    label = Label.LABEL4;
                    continue;

                case LABEL3:
                    print(label);
                    label = Label.LABEL1;
                    break;

                case LABEL4:
                    print(label);
                    label = Label.LABEL3;
                    continue;
            }
            break;
        }
    }

    public final static void print(Label label){
        System.out.println(label);
    }
3 голосов
/ 07 февраля 2013

StephenC пишет:

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

Еще один ...

Мэтт Вулф пишет:

Люди всегда говорят о том, что никогда не используют goto, но я думаю, что есть действительно хороший пример использования в реальном мире, который довольно хорошо известен и используется .. Это значит, что нужно выполнить некоторый код перед возвратом из функция .. Обычно это освобождение блокировки или что нет, но в моем случае я бы люблю иметь возможность прыгать на перерыв прямо перед возвращением, чтобы я мог сделать требуется обязательная очистка.

try {
    // do stuff
    return result;  // or break, etc.
}
finally {
    // clean up before actually returning, even though the order looks wrong.
}

http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html

Блок finally всегда выполняется при выходе из блока try. это гарантирует, что блок finally выполняется, даже если неожиданный исключение происходит. Но, наконец, полезно не только для исключения обработка - это позволяет программисту избежать очистки кода случайно обойден при возврате, продолжении или разрыве. Выкладываю зачистку код в блоке finally - это всегда хорошая практика, даже если нет ожидаются исключения.

Глупый вопрос об интервью, связанный с finally: следующий: если вы вернетесь из блока try {}, но у вас тоже есть результат в вашем finally {}, какое значение возвращается?

1 голос
/ 17 марта 2014

Самый простой:

int label = 0;
loop:while(true) {
    switch(state) {
        case 0:
            // Some code
            state = 5;
            break;

        case 2:
            // Some code
            state = 4;
            break;
        ...
        default:
            break loop;
    }
}
0 голосов
/ 08 декабря 2014

Java не имеет goto, потому что это делает код неструктурированным и неясным для чтения. Однако вы можете без проблем использовать break и continue как цивилизованную форму goto .


Прыжок вперед с помощью разрыва -

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 block
System.out.println("After ahead");

выход

Before Break
After ahead

Переход назад с помощью продолжения

before: {
    System.out.println("Continue");
    continue before;
}

Это приведет к бесконечному циклу , так как каждый раз, когда строка continue before выполняется, поток кода снова начинается с before.

0 голосов
/ 25 ноября 2014

Используйте помеченный разрыв как альтернативу goto.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...