Переключение без перерыва

У меня есть оператор switch, как показано ниже. Обратите внимание, что перерыва нет. Findbugs сообщает об ошибке только во втором операторе case. Ошибка: найден оператор Switch, в котором один случай переходит в другой.

switch(x) {

    case 0:
        // code

    case 1:
        // code

    case 2:
        // code
}

person fastcodejava    schedule 19.12.2011    source источник
comment
что говорит сообщение об ошибке?   -  person jmj    schedule 19.12.2011
comment
А проблема...?   -  person fge    schedule 19.12.2011
comment
Вы уверены, что это не предупреждение, а ошибка?   -  person xagyg    schedule 20.12.2011
comment
Мне любопытно, если это недовольно, потому что в приведенном выше коде нет значения по умолчанию:.   -  person Miguel Pereira    schedule 26.09.2013


Ответы (6)


Findbugs отмечает, что переход от одного case к другому, как правило, не является хорошей идеей, если в первом есть какой-либо код (хотя иногда его можно использовать с пользой). Поэтому, когда он видит второй case и не break, он сообщает об ошибке.

Так, например:

switch (foo) {
    case 0:
        doSomething();
    case 1:
        doSomethingElse();
    default:
        doSomeOtherThing();
}

Это вполне допустимый Java, но он, вероятно, не делает то, что задумал автор: если foo равно 0, все три функции doSomething, doSomethingElse и doSomeOtherThing запускаются (в таком порядке). Если foo равно 1, запускаются только doSomethingElse и doSomeOtherThing. Если foo является любым другим значением, выполняется только doSomeOtherThing.

В отличие:

switch (foo) {
    case 0:
        doSomething();
        break;
    case 1:
        doSomethingElse();
        break;
    default:
        doSomeOtherThing();
        break;
}

Здесь будет работать только одна из функций, в зависимости от значения foo.

Поскольку забывание break является распространенной ошибкой кодирования, такие инструменты, как Findbugs, помечают это за вас.

Существует распространенный вариант использования, когда у вас есть несколько операторов case в строке с никаким промежуточным кодом:

switch (foo) {
    case 0:
    case 1:
        doSomething();
        break;
    case 2:
        doSomethingElse();
        break;
    default:
        doSomeOtherThing();
        break;
}

Там мы хотим вызвать doSomething, если foo равно 0 или 1. Большинство инструментов не пометят это как возможную ошибку кодирования, потому что в case 0 нет кода до case 1, и это довольно распространенный шаблон.

person T.J. Crowder    schedule 19.12.2011
comment
Действительно ли необходимо break в конце? - person kcpr; 04.01.2016
comment
@kcpr: Строго говоря, вам не нужен break; в последнем случае переключателя (default выше). Но я предпочитаю постоянство. Это чисто вопрос стиля, в любом случае создается один и тот же байт-код. (Конечно, это неверно, если вы опустите любое другое break;.) - person T.J. Crowder; 05.01.2016
comment
Это очень хорошее описание общей проблемы и того, как работает переключатель, но на самом деле он не отвечает на вопрос ОП - хотя, строго говоря, ОП на самом деле не задавал вопрос... - person Tasgall; 26.07.2017
comment
@Tasgall: Как это не отвечает на вопрос? (Как есть. :-)) - person T.J. Crowder; 26.07.2017
comment
T.J.Crowder: Вопрос — ну, технически он не задавал вопрос, но его ситуация и формулировка соответствуют вопросу, который я искал, по крайней мере :P, — заключался в том, как заставить инструмент поиска ошибок не жаловаться на преднамеренное падение. Ваша запись о коммутаторе и о том, почему findbugs помечает это, очень хороша и тщательна, но не показывает, как отключить предупреждение, если оно было преднамеренным. Ответ @ L2M ниже (добавление // провала или какого-либо варианта, где в противном случае был бы разрыв) исправил это для меня. Не могли бы вы добавить это решение к своему ответу? - person Tasgall; 07.09.2017
comment
@Tasgall: ответ L2M интересен, я всегда добавляю ложный комментарий из соображений стиля. Вы говорите, что наличие комментария заставляет FindBugs не сообщать о проблеме? - person T.J. Crowder; 07.09.2017
comment
Почему это логично для java? Выполнение случая, утверждение которого является ложным только потому, что предыдущий случай был верным? - person Emanuel Graf; 05.01.2018
comment
@EmanuelGraf: именно так обычно работают операторы switch. Java унаследовал это от C. И обратите внимание, что если у вас есть два оператора case без разрыва, вы явно говорите Do this для любого из вышеперечисленных, поэтому это не означает, что вы выполняете код для случая, который является ложным. Это то, что вы выполняете код для одного из двух (или более) случаев, для которых он вам нужен. (C# модифицирует switch, чтобы разрешить стековые метки case только в том случае, если между ними нет кода.) - person T.J. Crowder; 05.01.2018
comment
Просто добавьте, что вы можете избежать break с другими операторами, такими как return doSomething(), но это предполагает, что вы находитесь внутри тела функции, и вы не хотите продолжать код над оператором switch-case. Так что на самом деле конкретный контекст. - person jafed; 31.01.2019

Я написал это как комментарии, но тогда это не видно. Я превращаю их в ответ. На самом деле это расширение ответа T.J.Crowder.

Вы можете найти соответствующее правило, которое заставляет Findbugs сообщать об ошибке здесь.

Вы можете запретить Findbugs сообщать об ошибках такого рода, создав файл xml со следующим содержимым, скажем, filter.xml, и запустив инструмент с параметром -exclude filter.xml. См. фильтры в Findbugs.

<FindBugsFilter>
  <Match>
    <Bug category="PERFORMANCE" />
  </Match>
</FindBugsFilter>
person melihcelik    schedule 19.12.2011
comment
В Stack Overflow на самом деле вполне приемлемо редактировать чужой ответ, чтобы добавить полезную информацию (например, выше). Действуй! - person T.J. Crowder; 19.12.2011
comment
@T.J.Crowder, к сожалению, мои правки были отклонены, мне пришлось восстановить свой ответ. В любом случае спасибо за предложение. - person melihcelik; 20.12.2011
comment
Я подозреваю, что фактическое правило было более общим SF_SWITCH_FALLTHROUGH. SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH означает, что не только произошел провал, но и было уничтожено то, что вы пытались сохранить в предыдущем случае. На мой взгляд, это скорее ошибка, поэтому я бы гораздо сомневался, стоит ли отключать правило DEAD_STORE, чем правило FALLTHROUGH. - person Kevin J. Chase; 05.04.2017

Сбои коммутатора подпадают под категорию Findbugs «хитрый код». Я думаю, что он помечает только первый случай провала в операторе switch, чтобы сократить количество сообщений об ошибках.

person Ted Hopp    schedule 19.12.2011

Без разрыва они попадут друг в друга, поэтому, если x == 0, вы пройдете весь код в каждом блоке оператора case. Findbugs может либо ошибаться в отношении ошибки, либо это может быть состояние ошибки без разрыва, то есть что-то в case 0 вызывает поломку чего-то в case 1.

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

person Matt Fellows    schedule 19.12.2011

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

Я не знаю, есть ли способ специально отключить предупреждение с помощью FindBugs, но инструмент Checkstyle распознает специальные комментарии, такие как /* fallthrough */ предположить, что пользователь действительно хочет, чтобы следующий код был выполнен. Размещение такого рода комментариев также улучшает читабельность. http://checkstyle.sourceforge.net/config_coding.html#FallThrough

В соглашении о коде Java также упоминается использование сквозного комментария. http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-142311.html

person L2M    schedule 25.04.2017

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

person Nisal Edu    schedule 01.11.2017