Оператор
Давайте немного подробнее рассмотрим нововведения для switch в разных версиях Java. Коротко их можно свести в таблицу:
switch в Java это оператор множественного выбора: он вычисляет выражение и сравнивает его по очереди с вариантами case, выполняя код совпавшего варианта. Его используют как удобную замену длинной цепочки if-else, когда веток больше двух и выбор идет по конкретному значению. Завершают ветку словом break, иначе выполнение «проваливается» в следующие case.
Представьте, что вы стоите на развилке, как богатырь с известной картины. Налево пойдешь, коня потеряешь, направо пойдешь, знания обретешь. Как запрограммировать такую ситуацию? Вы уже, скорее всего, знаете, что подобный выбор мы совершаем с помощью конструкций if-then и if-then-else.Кратко
switchэто оператор множественного выбора: он сравнивает значение выражения с вариантамиcaseи выполняет совпавший.- Удобен, когда веток больше двух, вместо длинной цепочки
if-else. - Выражение может быть
byte,short,char,int, их обертками,String(с Java 7) иEnum. breakзавершает switch; без него выполнение «проваливается» в следующие case (fall-through).- Несколько
caseподряд позволяют выполнить один код для разных значений. - В новых версиях switch стал выражением: стрелочный синтаксис
case X -> ...(Java 12+) и возврат значения черезyield(Java 13+).
if (turn_left) {
System.out.println(«Коня потеряешь»);
}
if (turn_right) {
System.out.println(“Знания обретешь”);
}
else
System.out.println(“Так и будешь стоять?”);
А что, если таких дорожек не две, а 10? Есть дорожка «совсем направо», «чуть левее», «еще чуть-чуть левее» и так далее, в количестве 10 штук?
Представьте, как разрастется ваш if-then-else код в таком варианте!
if (вариант1)
{… }
else if (вариант2)
{…}
…
else if (вариантN) ….
Итак, у вас не одна развилка условий, а несколько, скажем, 10 (тут важно, что число развилок ограничено). Для таких ситуаций есть специальный оператор выбора, switch case java.
Синтаксис switch
switch (ВыражениеДляВыбора) {
case (Значение1):
Код1;
break;
case (Значение2):
Код2;
break;
...
case (ЗначениеN):
КодN;
break;
default:
КодВыбораПоУмолчанию;
break;
}
Порядок выполнения
Порядок выполнения в операторе следующий:- Вычисляется ВыражениеДляВыбора. Далее оператор switch сравнивает полученное выражение с очередным Значением (в порядке перечисления).
- Если ВыражениеДляВыбора совпало со Значением, то выполняется код, идущий после двоеточия.
- Если встречается конструкция break, то управление передается за пределы команды switch.
- Если совпадений ВыражениеДляВыбора и Значений не выявлено, то управление передается КодуВыбораПоУмолчанию.
Важные моменты
Тип ВыражениеДляВыбора для оператора выбора switch в Java должен быть одним из следующих:
- byte, short, char, int.
- Их обертки Byte, Short, Character, Integer.
- String (начиная с Java 7).
- Перечисление (Enum).
- блок default необязательный, тогда в случае отсутствия совпадений ВыраженияДляВыбора и Значений не будет выполнено никаких действий.
- break не является обязательным, если его нет, код продолжит выполнение (игнорируя дальнейшие сравнения значений в блоках case) до первого встреченного
breakили до конца оператора switch. - если необходимо выполнять один и тот же код для нескольких вариантов выбора, для исключения дублирования перед ним указываем несколько соответствующих значений в подряд идущих блоках case.
Перейдем к практике использования оператора switch в Java
Не волнуйтесь, с теорией покончили, и, после дальнейших примеров станет все гораздо понятнее. Итак, приступим. Давайте рассмотрим пример из астрономии о планетах Солнечной системы. В соответствии с последними международными положениями исключим Плутон (за свойства ее орбиты). Вспомним, что планеты у нас расположены от Солнца в следующей последовательности: Меркурий, Венера, Земля, Марс, Юпитер, Сатурн, Уран и Нептун. Создадим Java метод, который получает на вход порядковый номер планеты (относительно удаленности от Солнца), а на выходе выдает основной состав атмосферы этой планеты в виде списка List<String>. Напомню, у некоторых планет схожий состав атмосферы. Так, Венера и Марс содержат в основном углекислый газ, у Юпитера и Сатурна она состоят из водорода и гелия, а Уран и Нептун вдобавок к последней паре газов также имеет метан. Наша функция:public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("Нет атмосферы");
break;
case 2:
case 4: result.add("Углекислый газ");
break;
case 3: result.add("Углекислый газ");
result.add("Азот");
result.add("Кислород");
break;
case 5:
case 6: result.add("Водород");
result.add("Гелий");
break;
case 7:
case 8: result.add("Метан");
result.add("Водород");
result.add("Гелий");
break;
default:
break;
}
return result;
}
Обратите внимание: идентичным по составу атмосфер планетам мы сопоставили один и тот же код. А сделали мы это за счет подряд идущих конструкций case.
Итого, если мы хотим получить состав атмосферы нашей с вами родной планеты, вызываем наш метод с параметром 3:
getPlanetAtmosphere(3).
System.out.println(getPlanetAtmosphere(3)) вернет нам [Углекислый газ, Азот, Кислород].
Эксперимент с break
Что получится, если мы уберем все операторы break? Попробуем на практике:
public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("Нет атмосферы");
case 2:
case 4: result.add("Углекислый газ");
case 3: result.add("Углекислый газ");
result.add("Азот");
result.add("Кислород");
case 5:
case 6: result.add("Водород");
result.add("Гелий");
case 7:
case 8: result.add("Метан");
result.add("Водород");
result.add("Гелий");
default:
}
return result;
}
Если мы распечатаем результат работы метода System.out.println(getPlanetAtmosphere(3)), то наша родная планета окажется не такой уж пригодной для жизни. Или пригодной? Судите сами:
[Углекислый газ, Азот, Кислород, Водород, Гелий, Метан, Водород, Гелий],
Почему так получилось? Программа выполнила все case после первого совпадения и до конца блока switch.
Излишняя оптимизация break
Заметим, что мы можем усовершенствовать метод иным расположением директив break и вариантов выбораpublic static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("Нет атмосферы");
break;
case 3: result.add("Азот");
result.add("Кислород");
case 2:
case 4: result.add("Углекислый газ");
break;
case 7:
case 8: result.add("Метан");
case 5:
case 6: result.add("Водород");
result.add("Гелий");
}
return result;
}
Выглядит короче, не так ли? Мы сократили общее число операторов, поигравшись с порядком следования case и перегруппировкой. Теперь каждый вид газа добавляется в список только в одной строчке кода.
Листинг последнего примера метода показан только для демонстрации работы, крайне не рекомендуется писать в подобном стиле. Если автору (а тем более сторонним программистам) схожего кода предстоит его сопровождать, то будет весьма тяжело восстановить логику формирования блоков выбора и выполняемого кода для оператора switch java.
Отличия от if
При внешней схожести операторов if и switch не забывайте, что выбор вариантов выполнения оператор множественного выбора switch основывает на КОНКРЕТНОМ ЗНАЧЕНИИ, тогда как в if может быть любое логическое выражение. Учитывайте данный факт, проектируя ваш код. Коротко разницу можно свести в таблицу:| Критерий | if | switch |
|---|---|---|
| Условие | любое логическое выражение | сравнение с конкретным значением |
| Число веток | любое | ограниченное, по значениям |
| Типы | любые условия | byte/short/char/int, обертки, String, Enum |
| Когда удобнее | сложные условия, диапазоны | много равнозначных вариантов по значению |
| Версия Java | Что появилось в switch |
|---|---|
| Java 7 | поддержка String в качестве выражения |
| Java 12 | стрелочный синтаксис case X ->, несколько значений через запятую, switch как выражение |
| Java 13 | возврат значения из switch через yield |
Switch в Java 7
До Java 7 в качестве значения для переключателя можно было использовать byte, short, char и int примитивы. Также была поддержка enum и оберток перечисленных выше примитивных типов: Character, Byte, Short, и Integer. Но ведь часто нам нужно найти значение java switch string! Как бы это выглядело в Java 6:DayOfWeek day = DayOfWeek.fromValue("Thursday");
switch (day) {
case MONDAY:
System.out.println("Today is windy !");
break;
case THURSDAY:
System.out.println("Today is sunny !");
break;
case WEDNESDAY:
System.out.println("Today is rainy!");
break;
default:
System.out.println("Oooops, something wrong !");
И enum:
public enum DayOfWeek {
MONDAY("Monday"),
THURSDAY("Thursday"),
WEDNESDAY("Wednesday"),
NOT_FOUND("Not found");
private final String value;
DayOfWeek(final String value) {
this.value = value;
}
public static DayOfWeek fromValue(String value) {
for (final DayOfWeek dayOfWeek : values()) {
if (dayOfWeek.value.equalsIgnoreCase(value)) {
return dayOfWeek;
}
}
return NOT_FOUND;
}
}
Но начиная с Java 7 добавилась возможно использовать тип String как значение для переключателя switch:
String day = "Thursday";
switch (day) {
case "Monday":
System.out.println("Today is windy !");
break;
case "Thursday":
System.out.println("Today is sunny !");
break;
case "Wednesday":
System.out.println("Today is rainy!");
break;
default:
System.out.println("Oooops, something wrong !");
}
Несмотря на новые возможности, подход с использованием enum более гибок и рекомендуем к использованию: этот enum мы можем переиспользовать много раз.
Для завершения такого прохода нужно либо полностью пройтись по всем элементам, либо использовать break или return.
Нововведение в java 12 дает нам возможность использовать лямбда оператор, который в свою очередь гарантирует , что будет выполнен только код справа от нее. Без всякого "проваливания".
Как будем выглядеть предыдущий пример в таком случае:
Break будет использоваться, но в тех ситуациях, когда нам не нужно ничего возвращать.
Switch в Java 12
В Java 12 были улучшены выражения Switch для сопоставления с образцом. Если использовать Switch как и в примере выше, для задания значения некоторой переменной мы должны были вычислить значение и присвоить заданной переменной, а затем использовать break:int count = 2;
int value;
switch (count) {
case 1:
value = 12;
break;
case 2:
value = 32;
break;
case 3:
value = 52;
break;
default:
value = 0;
}
Но благодаря возможностям 12-й версии Java мы можем переписать данное выражение следующим образом:
int value = switch (count) {
case 1:
break 12;
case 2:
break 32;
case 3:
break 52;
default:
break 0;
};
Давайте немного пройдемся по измененным моментам:
Если ранее мы задавали переменной значение внутри блоков case, так как сам оператор switch не мог ничего возвращать, сейчас такая возможность у нас есть, и мы непосредственно с помощью switch возвращаем значение.
Ранее справа от break у нас уже ничего не могло стоять, а сейчас мы его используем как оператор return для возвращения значения нашим switch. Метки с двоеточием отмечают точку входа в блок операторов. То есть с того места начинается выполнение всего кода ниже, даже тогда, когда встречается другая метка.
Как итог, сквозной переход от метки к метке, который еще называют проваливанием (fall-through).
int count = 2;
int value = switch (count) {
case 1 -> 12;
case 2 -> 32;
case 3 -> 52;
default -> 0;
};
Код стал значительно проще, не правда ли?
И еще: лямбда оператор может служить и типичным аналогом двоеточия, после которого идет целый блок с некоторыми операциями:
int count = 2;
int value = switch (count) {
case 1 -> {
//некоторые вычислительные операции...
break 12;
}
case 2 -> {
//некоторые вычислительные операции...
break 32;
}
case 3 -> {
//некоторые вычислительные операции...
break 52;
}
default -> {
//некоторые вычислительные операции...
break 0;
}
};
Ну а что если у нас для некоторых кейсов возвращаемое значение будет одинаковым?
Получится, что у нас фактически одинаковые кейсы для некоторых разных значений. Как бы это можно было бы сократить с помощью новых возможностей Java 12:
int count = 2;
int value = switch (count) {
case 1, 3, 5 -> 12;
case 2, 4, 6 -> 52;
default -> 0;
};Switch в Java 13
В Java 13 изменился способ возврата значения из switch. Если в java 12 возвращаемое значение мы писали после break, который служил у нас как return для блока switch, то сейчас возвращать значение мы будем с помощью слова yield. Смотрим:int value = switch (count) {
case 1:
yield 12;
case 2:
yield 32;
case 3:
yield 52;
default:
yield 0;
};
В тоже время, код, написанный на java 12 c использованием break для возврата, компилироваться не будет((
Итого
- Используйте оператор case при числе ветвлений более двух, чтобы не загромождать if-структурами код.
- Не забывайте завершать логический блок каждой ветки соответствующего конкретному значению (блок case) вызовом break.
- Оператор switch помимо некоторых примитивных типов, в качестве выражения может использовать также типы Enum и String.
- Помните про блок default, употребляйте его для обработки незапланированных значений выбора.
- Для оптимизации производительности переместите ветки кода с наиболее часто встречающимися вариантами выбора к началу блока switch.
- Не увлекайтесь «оптимизацией» за счет удаления break в конце блока выбора case, такой код сложен для понимания, и, как следствие, тяжело сопровождать при его развитии.
Читайте также
- 8 частых ошибок начинающих программистов: одна из них это как раз пропуск
breakв switch. - Тернарный оператор: короткая запись выбора из двух вариантов.
- Класс ArrayList: список
List, который мы заполняем в примерах.
Вопросы и ответы
Что такое оператор switch в Java?
Это оператор множественного выбора: он вычисляет выражение и сравнивает его с вариантамиcase, выполняя код совпавшего варианта. Удобен как замена длинной цепочки if-else.
Когда использовать switch вместо if?
Когда веток больше двух и выбор идет по конкретному значению переменной. Для сложных логических условий или диапазонов лучше подходитif.
Какие типы можно использовать в switch?
byte, short, char, int, их обертки (Byte, Short, Character, Integer), String (с Java 7) и перечисления Enum.
Зачем нужен break в switch?
break завершает switch после выполнения нужного case. Без него выполнение «проваливается» в следующие case подряд (fall-through), что часто приводит к ошибкам.
Что такое проваливание (fall-through) в switch?
Это когда после совпавшего case, где нетbreak, выполняются все следующие case до конца switch или до первого break. Иногда используется намеренно (несколько case подряд), но чаще это ошибка.
Что нового в switch в новых версиях Java?
С Java 7 можно использоватьString. С Java 12 появился стрелочный синтаксис case X -> без проваливания и switch как выражение. С Java 13 значение из switch возвращают через yield.