Почему компилятор не помещает автоматически операторы break после каждого блока кода в переключателе? По историческим причинам? Когда вы хотите, чтобы выполнялось несколько блоков кода?
94
Почему компилятор не помещает автоматически операторы break после каждого блока кода в переключателе? По историческим причинам? Когда вы хотите, чтобы выполнялось несколько блоков кода?
break
.Ответы:
Иногда полезно иметь несколько вариантов, связанных с одним и тем же блоком кода, например
case 'A': case 'B': case 'C': doSomething(); break; case 'D': case 'E': doSomethingElse(); break;
и т.д. Просто пример.
По моему опыту, обычно плохой стиль - «проваливаться» и выполнять несколько блоков кода для одного случая, но в некоторых ситуациях это может быть использовано.
источник
// Intentional fallthrough.
когда пропускаете перерыв. На мой взгляд, это не столько плохой стиль, сколько «случайно забыть перерыв». PS Конечно не в простых случаях, как в самом ответе.case
s складываются таким образом. Если между ними есть код, тогда да, комментарий, вероятно, заслуживает внимания.case
, например:,case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();
без необходимости перерыва между вариантами. Паскаль мог это сделать: «Оператор case сравнивает значение порядкового выражения с каждым селектором, который может быть константой, поддиапазоном или их списком, разделенным запятыми». ( wiki.freepascal.org/Case )Исторически сложился так , что это потому , что
case
по существу , определяющее направлениеlabel
, также известное как целевая точка в видеgoto
вызова. Оператор switch и связанные с ним случаи на самом деле просто представляют собой многостороннюю ветвь с несколькими потенциальными точками входа в поток кода.Все это было замечено почти бесконечное количество раз, что
break
почти всегда является поведением по умолчанию, которое вы бы предпочли иметь в конце каждого случая.источник
Java происходит от C, и это синтаксис от C.
Бывают случаи, когда вы хотите, чтобы несколько операторов case имели только один путь выполнения. Ниже приведен образец, который покажет вам, сколько дней в месяце.
class SwitchDemo2 { public static void main(String[] args) { int month = 2; int year = 2000; int numDays = 0; switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: numDays = 31; break; case 4: case 6: case 9: case 11: numDays = 30; break; case 2: if ( ((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0) ) numDays = 29; else numDays = 28; break; default: System.out.println("Invalid month."); break; } System.out.println("Number of Days = " + numDays); } }
источник
Я считаю это ошибкой. В качестве языковой конструкции ее так же легко использовать
break
по умолчанию и вместо нее использоватьfallthrough
ключевое слово. Большая часть кода, который я написал и прочитал, имеет перерыв после каждого случая.источник
continue <case name>
который позволяет явно указать, с каким оператором case продолжить;case
пределах токаswitch
это просто становитсяgoto
. ;-)С провалом футляра можно делать разные интересные вещи.
Например, допустим, вы хотите выполнить определенное действие для всех случаев, но в определенном случае вы хотите выполнить это действие плюс что-то еще. Использование оператора switch с провалом сделало бы это довольно легко.
switch (someValue) { case extendedActionValue: // do extended action here, falls through to normal action case normalActionValue: case otherNormalActionValue: // do normal action here break; }
Конечно, легко забыть
break
утверждение в конце кейса и вызвать неожиданное поведение. Хорошие компиляторы предупредят вас, если вы опустите оператор break.источник
Не говоря уже о хорошем желании иметь возможность использовать один и тот же блок для нескольких случаев (которые могут быть особенными) ...
Это в основном для совместимости с C, и, возможно, это древний прием с тех времен, когда
goto
ключевые слова бродили по земле. Конечно, он делает возможным некоторые удивительные вещи, такие как Устройство Даффа , но, аргумент ли это за или против, в лучшем случае… аргумент.источник
break
После того, как переключательcase
s используется , чтобы избежать проваливается в отчетности коммутатора. Хотя, что интересно, теперь это может быть достигнуто с помощью недавно сформированных меток переключателей, реализованных через JEP-325 .С этими изменениями можно избежать
break
каждого переключенияcase
, как показано далее: -public class SwitchExpressionsNoFallThrough { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int value = scanner.nextInt(); /* * Before JEP-325 */ switch (value) { case 1: System.out.println("one"); case 2: System.out.println("two"); default: System.out.println("many"); } /* * After JEP-325 */ switch (value) { case 1 ->System.out.println("one"); case 2 ->System.out.println("two"); default ->System.out.println("many"); } } }
На выполнение вышеуказанного кода с JDK-12 , то сравнительный выход можно рассматривать как
//input 1 // output from the implementation before JEP-325 one two many // output from the implementation after JEP-325 one
а также
//input 2 // output from the implementation before JEP-325 two many // output from the implementation after JEP-325 two
и конечно же вещь без изменений
// input 3 many // default case match many // branches to 'default' as well
источник
Таким образом, вам не нужно повторять код, если вам нужно несколько случаев, чтобы сделать одно и то же:
case THIS: case THAT: { code; break; }
Или вы можете сделать что-то вроде:
case THIS: { do this; } case THAT: { do that; }
Каскадным способом.
На самом деле подвержен ошибкам / путанице, если вы спросите меня.
источник
do this
иdo that
для этого, но толькоdo that
для этого?Судя по историческим данным, Тони Хоар изобрел утверждение случая в 1960-х годах, во время революции «структурного программирования». Оператор case Тони поддерживает несколько меток для каждого случая и автоматический выход без каких-либо вонючих
break
операторов. Требование явного характераbreak
исходило из линии BCPL / B / C. Деннис Ричи пишет (в ACM HOPL-II):Мне не удалось найти никаких исторических писаний о BCPL, но комментарий Ричи предполагает, что это
break
было более или менее исторической случайностью. Позднее BCPL устранил проблему, но, возможно, Ричи и Томпсон были слишком заняты изобретением Unix, чтобы беспокоиться о таких деталях :-)источник
break
позволяет «выполнять несколько блоков кода», и его больше заботит мотивация этого выбора дизайна. Другие упомянули хорошо известное наследие от C до Java, и этот ответ подтолкнул исследование еще дальше к дням, предшествующим C. Хотелось бы, чтобы у нас с самого начала было это (хотя и очень примитивное) сопоставление с образцом.Java происходит от языка C, наследие которого включает технологию, известную как устройство Даффа . Это оптимизация, основанная на том факте, что контроль переходит от одного случая к другому в отсутствие
break;
оператора. К тому времени, когда C был стандартизирован, такого кода «в дикой природе» было достаточно, и было бы контрпродуктивно менять язык, чтобы разрушить такие конструкции.источник
Как говорили раньше, это позволяет избежать провала, и это не ошибка, это особенность. Если
break
вас раздражает слишком много утверждений, вы можете легко избавиться от них, используяreturn
вместо них инструкции. На самом деле это хорошая практика, потому что ваши методы должны быть как можно меньше (для удобства чтения и поддержки), поэтомуswitch
оператор уже достаточно большой для метода, следовательно, хороший метод не должен содержать ничего другого, это пример:public class SwitchTester{ private static final Log log = LogFactory.getLog(SwitchTester.class); public static void main(String[] args){ log.info(monthsOfTheSeason(Season.WINTER)); log.info(monthsOfTheSeason(Season.SPRING)); log.info(monthsOfTheSeason(Season.SUMMER)); log.info(monthsOfTheSeason(Season.AUTUMN)); } enum Season{WINTER, SPRING, SUMMER, AUTUMN}; static String monthsOfTheSeason(Season season){ switch(season){ case WINTER: return "Dec, Jan, Feb"; case SPRING: return "Mar, Apr, May"; case SUMMER: return "Jun, Jul, Aug"; case AUTUMN: return "Sep, Oct, Nov"; default: //actually a NullPointerException will be thrown before reaching this throw new IllegalArgumentException("Season must not be null"); } } }
На казнь печатается:
12:37:25.760 [main] INFO lang.SwitchTester - Dec, Jan, Feb 12:37:25.762 [main] INFO lang.SwitchTester - Mar, Apr, May 12:37:25.762 [main] INFO lang.SwitchTester - Jun, Jul, Aug 12:37:25.762 [main] INFO lang.SwitchTester - Sep, Oct, Nov
как и ожидалось.
источник
Отсутствие автоматического прерывания, добавленного компилятором, позволяет использовать переключатель / регистр для проверки условий, например,
1 <= a <= 3
путем удаления оператора break из 1 и 2.switch(a) { case 1: //I'm between 1 and 3 case 2: //I'm between 1 and 3 case 3: //I'm between 1 and 3 break; }
источник
потому что есть ситуации, когда вы хотите пройти через первый блок, например, чтобы избежать написания одного и того же кода в нескольких блоках, но все же иметь возможность разделить их для управления mroe. Есть и масса других причин.
источник
Это старый вопрос, но на самом деле сегодня я столкнулся с использованием case без оператора break. На самом деле не использовать break очень полезно, когда вам нужно последовательно комбинировать разные функции.
например, использование кодов ответа http для аутентификации пользователя с помощью токена времени
код ответа сервера 401 - токен устарел -> повторно создать токен и войти в систему.
Код ответа сервера 200 - токен в порядке -> войти в систему.
в случае утверждения:
case 404: case 500: { Log.v("Server responses","Unable to respond due to server error"); break; } case 401: { //regenerate token } case 200: { // log in user break; }
Используя это, вам не нужно вызывать пользовательскую функцию журнала для ответа 401, потому что, когда токен регенерируется, среда выполнения переходит к делу 200.
источник
Вы можете легко выделить другой тип числа, месяц, счет.
Это лучше, если в этом случае;
public static void spanishNumbers(String span){ span = span.toLowerCase().replace(" ", ""); switch (span){ case "1": case "jan": System.out.println("uno"); break; case "2": case "feb": System.out.println("dos"); break; case "3": case "mar": System.out.println("tres"); break; case "4": case "apr": System.out.println("cuatro"); break; case "5": case "may": System.out.println("cinco"); break; case "6": case "jun": System.out.println("seis"); break; case "7": case "jul": System.out.println("seite"); break; case "8": case "aug": System.out.println("ocho"); break; case "9": case "sep": System.out.println("nueve"); break; case "10": case "oct": System.out.println("diez"); break; } }
источник
Сейчас я работаю над проектом, в котором мне нужен
break
оператор switch, иначе код не будет работать. Потерпите меня, и я дам вам хороший пример того, зачем вам нуженbreak
оператор switch.Представьте, что у вас есть три состояния: одно ожидает, пока пользователь введет число, второе - вычислит его, а третье - напечатает сумму.
В этом случае у вас есть:
Глядя на состояния, вы бы хотели, чтобы порядок действий начинался с состояния1 , затем состояния3 и, наконец, состояния2 . В противном случае мы будем печатать только вводимые пользователем данные без вычисления суммы. Чтобы еще раз прояснить это, мы ждем, пока пользователь вводит значение, затем вычисляем сумму и распечатываем сумму.
Вот пример кода:
while(1){ switch(state){ case state1: // Wait for user input code state = state3; // Jump to state3 break; case state2: //Print the sum code state = state3; // Jump to state3; case state3: // Calculate the sum code state = wait; // Jump to state1 break; } }
Если мы не используем
break
, он будет выполняться в следующем порядке: state1 , state2 и state3 . Но используяbreak
, мы избегаем этого сценария и можем заказать правильную процедуру, которая должна начинаться с состояния1, затем состояния3 и последнего, но не менее важного состояния2.источник
Именно потому, что при некотором умном размещении вы можете выполнять блоки каскадом.
источник