Почему Java допускает помеченные разрывы для произвольных операторов? - PullRequest
13 голосов
/ 24 февраля 2011

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

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

    if (doneExecutingThisBlock())
        break myBlock;

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

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

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

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

Ответы [ 8 ]

10 голосов
/ 24 февраля 2011

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

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

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

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

2 голосов
/ 24 февраля 2011

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

2 голосов
/ 24 февраля 2011

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

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

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

Я никогда не видел такого в использовании, и за 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.
        }
    }
}
1 голос
/ 24 февраля 2011

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

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

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

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

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

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

1 голос
/ 24 февраля 2011

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

Рассмотрим:

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

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

А зачем вообще такое поведение?

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

0 голосов
/ 24 февраля 2011

Добавляя к ответу Стивена С. 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();
}}
0 голосов
/ 24 февраля 2011

Оператор break завершает помеченный оператор;он не передает поток управления на этикетку.Поток управления переносится в оператор сразу после помеченного (прекращенного) оператора.

Похоже, полезно выходить из вложенных циклов.См. http://download.oracle.com/javase/tutorial/java/nutsandbolts/branch.html

Это семантически то же самое, что и , есть ли эквивалент Java-разрыва в C # или обходной путь

0 голосов
/ 24 февраля 2011

Это «структурированный» эквивалент 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;
        }
    ...
...