Почему компилятор не помещает автоматически операторы 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.
Почему компилятор не помещает автоматически операторы 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, отсутствовал в языке, когда мы изучали его в 1960-х годах, и поэтому перегрузка ключевого слова break для выхода из оператора переключения B и C является результатом расходящейся эволюции, а не сознательной изменение.
Мне не удалось найти никаких исторических писаний о 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;
}
Это старый вопрос, но на самом деле сегодня я столкнулся с использованием 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.
break
.