Почему Java допускает помеченные перерывы на произвольных операторах?

Недавно я узнал, что следующий Java-код совершенно легален:

myBlock: {
    /* ... code ... */

    if (doneExecutingThisBlock())
        break myBlock;

    /* ... more code ... */
}

Обратите внимание, что myBlock не является циклом - это всего лишь блок кода, который я ограничил фигурными фигурными скобками.

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

Мой вопрос таков: есть ли веская причина для этого дизайнерского решения?. Поэтому сделайте так, чтобы вы могли только вырваться из определенных закрывающих операторов, используя помеченные break, но не регулярные break s? И зачем вообще допускать такое поведение? Учитывая, насколько (сравнительно) хорошо разработанная Java является языком, я бы предположил, что для этого есть причина, но я, честно говоря, не могу думать об этом.

Ответы

Ответ 1

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

Теперь мы можем оглянуться назад и судить об этом выборе. Это приносит пользу программистам, давая им дополнительную силу выражения? Кажется, очень мало, функция редко используется. Не стоит ли программистам учиться и понимать? Похоже, это подтверждается этим обсуждением.

Если бы ты мог вернуться и изменить его, не так ли? Я не могу сказать, что буду. У нас есть фетиш для общности.

Если в параллельном юниверсе он ограничивался только операторами циклов, есть еще шанс, возможно, намного меньше, что кто-то ставит вопрос о stackoverflow: почему он не может работать с произвольными утверждениями?

Ответ 2

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

Ответ 3

Проблема с goto заключается в том, что она может перепрыгнуть вперед, прошлый код. Отмеченный перерыв не может этого сделать (он может только вернуться назад). IIRC С++ имеет дело с goto, прыгающим мимо кода (прошло более 17 лет с тех пор, как я об этом позаботился, хотя я не уверен, что я правильно помню).

Java была разработана для использования программистами C/С++, поэтому многие вещи были сделаны, чтобы ознакомить их с этими разработчиками. Можно сделать разумный перевод с C/С++ на Java (хотя некоторые вещи не являются тривиальными).

Разумно подумать, что они поместили это на язык, чтобы дать разработчикам C/С++ безопасный goto (где вы можете только вернуться в коде), чтобы сделать его более удобным для некоторых программистов, переходящих.

Я никогда не видел этого в использовании, и я редко видел помеченный перерыв вообще в 16+ годах программирования Java.

Вы не можете прорваться вперед:

public class Test 
{
    public static void main(final String[] argv) 
    {
        int val = 1;

        X:
        {
            if(argv.length == 0)
            {
                break X;
            }

            if(argv.length == 1)
            {
                break Y;   <--- forward break will not compile
            }
        }

        val = 0;

        Y:
        {
            Sysytem.out.println(val); <-- if forward breaks were allowed this would 
                                          print out 1 not 0.
        }
    }
}

Ответ 4

Зачем делать так, чтобы вы могли только вырваться из определенных охватывающих операторов, используя помеченные перерывы, но не регулярные разрывы

Рассмотрим:

while (true) {
    if (condition) {
        break;
    }
}

Если break сделал, как вы предлагаете, этот код будет работать неожиданно. Перерывы станут намного сложнее в использовании.

И зачем вообще допускать это поведение?

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

Ответ 5

Есть ли веская причина для этого дизайнерского решения?

Да. Потому что он работает.

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

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

while (...) {
     /* ... */
     if (something) break;
     /* ... */
}

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

Ответ 6

Это "структурированный" эквивалент goto, полезный при определенных обстоятельствах.

Я довольно часто использую такой ярлык для создания именованных подблоков в методе, чтобы строго ограничить область переменных или просто наметить блок кода, который не подходит для разрыва в отдельную функцию. То есть я использую его для маркировки блока, так что структура кода вокруг фигурных скобок сохраняется. Вот пример в C для вызова JNI, и я делаю то же самое в Java:

JNIEXPORT void JNICALL Java_xxx_SystemCall_jniChangePassword(JNIEnv *jep, jobject thsObj,
 jlong handle, jbyteArray rndkey, jbyteArray usrprf, jbyteArray curpwd, jbyteArray newpwd, jint pwdccs, jint tmosec) {
    Message                             rqs,rpy;

    thsObj=thsObj;

    SetupRequest: {
        memset(&rqs,0,sizeof(rqs));
        setOpcode(&rqs,"CHGPWD");
        if(!setField(mFldAndLen(rqs.rnd               ),null                      ,jep,rndkey,"Random Key")) {
            return;
            }
        if(!setField(mFldAndLen(rqs.dta.chgpwd.user   ),&rqs.dta.chgpwd.userLen   ,jep,usrprf,"User Profile")) {
            return;
            }
        if(!setField(mFldAndLen(rqs.dta.chgpwd.curPass),&rqs.dta.chgpwd.curPassLen,jep,curpwd,"Cur Password")) {
            return;
            }
        if(!setField(mFldAndLen(rqs.dta.chgpwd.newPass),&rqs.dta.chgpwd.newPassLen,jep,newpwd,"New Password")) {
            return;
            }
        rqs.dta.chgpwd.ccsid=pwdccs;
        }
    ...

Ответ 7

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

Кажется, полезно выйти из вложенных циклов. См. http://download.oracle.com/javase/tutorial/java/nutsandbolts/branch.html

Это семантически то же самое, что есть эквивалент помеченного ярлыком Java в С# или обходном пути

Ответ 8

Добавляя к Stephen C answer, if (something), вы не можете вырваться из вложенного цикла. Эти ситуации происходят в числовых алгоритмах. Один простой пример здесь - вы не можете вырваться из i-цикла без названного. Надеюсь, это поможет.

public class JBreak  {

    private int brj;

    public JBreak (String arg) {
        brj = Integer.parseInt (arg);       
    }

    public void print () {
        jbreak:
        for (int i = 1 ; i < 3 ; i++) {
            for (int j = 0 ; j < 5 ; j++) {
                if ((i*j) == brj)
                    break jbreak;
                System.out.println ("i,j: " + i + "," + j);
    }}}

    public static void main (String[] args) {
        new JBreak(args[0]).print();
}}