Оператор switch-case в Java
В этой статье мы поговорим с Вами об операторе switch в Java. Оператор switch в Java — это условный оператор, который дает возможность сравнивать переменную со списком значений.
В Java 14 были внесены значительные изменения в его синтаксис. Мы с Вами рассмотрим, как выглядел блок switch до Java 14, а также какие изменения были внесены в четырнадцатой версии.
1. Классический switch (до Java 14)
Давайте рассмотрим switch на примере — задается число от 1 до 3. Если 1 — включается зеленый цвет светофора, 2 — желтый и 3 — красный. Если любое другое число, то выполнится блок кода default:
public class TrafficLight {
public static void main(String[] args) {
int x = 3;
switch (x) {
case 1:
System.out.println("Зеленый");
break;
case 2:
System.out.println("Желтый");
break;
case 3:
System.out.println("Красный");
break;
default:
System.out.println("Неправильно введено число");
break;
}
}
} Общая форма написания оператора switch:
switch(выражение) {
case значение1:
// Блок кода 1
break;
case значение2:
// Блок кода 2
break;
...
case значениеN:
// Блок кода N
break;
default:
// Блок кода для default
} В круглых скобках мы указываем выражение, значение которого будет сравниваться со списком значений, перечисленных после ключевого слова case. Если выражение совпадает со значением1, то выполняется блок кода, указанный после значение1.
Выражение в switch должно иметь тип char, byte, short, int, enum (начиная с Java 6) или String (начиная с Java 7). Использование любого другого типа, например, float, long или boolean, приведет к ошибке компиляции.
Оператор switch может только проверять на равенство. Такие операторы, как >= и <=, недопустимы.
Для нескольких значений case можно указывать один блок, как показано в следующем примере:
public class SwitchExample1 {
public static void main(String[] args) {
int month = 4;
String season;
switch (month) {
case 12:
case 1:
case 2:
season = "Winter";
break;
case 3:
case 4:
case 5:
season = "Spring";
break;
case 6:
case 7:
case 8:
season = "Summer";
break;
case 9:
case 10:
case 11:
season = "Autumn";
break;
default:
season = "Not a Month";
}
System.out.println("April is in the " + season + ".");
}
}
Если выражение в switch имеет тип, меньший чем int (byte, short или char), case константа должна соответствовать этому типу. Например, следующий пример не откомпилируется, потому что значение 129 выходит за границы типа byte:
byte number = 2;
switch (number) {
case 13:
case 129: //compiler error
} Также нельзя использовать несколько case констант с одним и тем же значением. Следующий блок кода не откомпилируется:
int number = 90;
switch (number) {
case 50:
System.out.println("50");
case 50:
System.out.println("50"); //compile error
case 140:
System.out.println("140");
default:
System.out.println("default");
} Выражение в switch может содержать Character, Byte, Short и Integer значения:
switch (Integer.valueOf(9)) {
case 9:
System.out.println("9");
} Case константы просматриваются сверху вниз, и первая совпавшая case константа в switch является точкой входа. Соответствующий ей блок и все последующие блоки будут выполняться — это называется проваливанием (fall-through). Например:
public class SwitchExample2 {
public static void main(String[] args) {
String str = "potato";
switch (str) {
case "tomato":
System.out.print("tomato ");
case "potato":
System.out.print("potato ");
case "cucumber":
System.out.print("cucumber ");
default:
System.out.println("any");
}
}
}
Результат выполнения программы:
potato cucumber any Для того чтобы выйти из switch, используйте ключевое слово break. Например, изменим предыдущий пример, чтобы выводилось только одно слово:
String str = "potato";
switch (str) {
case "tomato":
System.out.print("tomato ");
break;
case "potato":
System.out.print("potato ");
break;
case "cucumber":
System.out.print("cucumber ");
break;
default:
System.out.println("any");
} Результат будет:
potato Секция default в switch обрабатывает все значения, которые не указаны явно в одной из case секций. Секция default может размещаться в любом месте, необязательно в конце. Если совпадений с case константами нет, точкой входа становится default, и проваливание продолжается в следующие за ним секции:
public class SwitchExample3 {
public static void main(String[] args) {
int z = 8;
switch (z) {
case 1:
System.out.println("Fall to one");
default:
System.out.println("default");
case 3:
System.out.println("Fall to three");
case 4:
System.out.println("Fall to four");
}
}
}
Начиная с Java 7 можно использовать объекты типа String в switch. Например:
String exam = "OCPJP 8";
switch (exam) {
case "OCPJP 7":
System.out.print(exam + ": 1Z0-804");
break;
case "OCPJP 8":
System.out.print(exam + ": 1Z0-809");
break;
default:
System.out.print(exam + ": ----");
break;
} Важно
Если переменная в switch равна null (например, String exam = null;), во время выполнения будет выброшено NullPointerException. Ошибки компиляции при этом нет, поэтому на таком примере часто ошибаются на собеседованиях. Перед switch проверяйте строку на null.
В следующем примере показано использование классического switch с перечислением (с enum). Обратите внимание: в метках case имя константы указывается без имени перечисления — BIG, а не CoffeeSize.BIG:
enum CoffeeSize { BIG, HUGE, OVERWHELMING }
public class CoffeeMachine {
public static void main(String[] args) {
CoffeeSize coffeeSize = CoffeeSize.BIG;
switch (coffeeSize) {
case BIG:
System.out.println("Цена: 2");
break;
case HUGE:
System.out.println("Цена: 3");
break;
case OVERWHELMING:
System.out.println("Цена: 4");
break;
}
}
} 2. Выражения switch в Java 14
И теперь давайте перейдем к тем изменениям, которые были внесены в Java 14. Если раньше switch был только оператором, то теперь появилось выражение switch (switch expression). Выражения switch впервые появились даже не в четырнадцатой, а в двенадцатой Java, но это была предварительная версия (preview). В Java 13 они были усовершенствованы, и в Java 14 они уже стали частью основной версии языка.
Выражения switch значительно упрощают код и делают его более читаемым благодаря стрелочному синтаксису (arrow case).
Давайте посмотрим на пример switch в Java 14:
public static void main(String[] args) {
int x = 2;
switch (x) {
case 1 -> System.out.println("Зеленый");
case 2 -> System.out.println("Желтый");
case 3 -> {
System.out.println("Красный");
System.out.println("Движение запрещено");
}
default -> System.out.println("Неправильно введено число");
}
} Результат работы программы:
Желтый Посмотрите, что здесь изменилось по сравнению с предыдущей версией. Теперь вместо двоеточия используется стрелочка — arrow case. И после стрелочки может быть либо одна строка, либо какой-то блок кода. Если это блок кода, то используются фигурные скобочки.
Также одно из изменений четырнадцатой версии Java — при стрелочном синтаксисе всегда выполняется только один блок. То есть проваливание в последующий блок кода, как это было со старой версией switch, не происходит. И поэтому уже нет необходимости писать break для каждого блока кода, как это было в старой версии.
Очень часто switch должен не только выполнить какой-то блок кода, но еще и вернуть значение:
public class SwitchExample2 {
public static void main(String[] args) {
int month = 4;
String season = switch (month) {
case 12, 1, 2 -> "Winter";
case 3, 4, 5 -> "Spring";
case 6, 7, 8 -> "Summer";
case 9, 10, 11 -> "Autumn";
default -> "Not a Month";
};
System.out.println("April is in the " + season + ".");
}
} Обратите внимание еще на одно новшество: несколько значений в одной метке case теперь можно перечислять через запятую — case 12, 1, 2.
В Java 14 было добавлено зарезервированное слово yield, которое возвращает значение из выражения switch, записанного со старым синтаксисом с двоеточием:
public class SwitchExample1 {
public static void main(String[] args) {
int month = 4;
String season = switch (month) {
case 12, 1, 2:
yield "Winter";
case 3, 4, 5:
yield "Spring";
case 6, 7, 8:
yield "Summer";
case 9, 10, 11:
yield "Autumn";
default:
yield "Not a Month";
};
System.out.println("April is in the " + season + ".");
}
} Слово yield также используется, когда в стрелочном синтаксисе нужно вернуть значение из блока кода в фигурных скобках:
CoffeeSize coffeeSize = CoffeeSize.BIG;
double price = switch (coffeeSize) {
case BIG -> 2;
case HUGE -> 3;
case OVERWHELMING -> {
System.out.println("Самый большой размер!");
yield 4;
}
}; Выражение switch обязано вернуть значение для любого возможного входного значения. Если тип выражения — enum и все константы перечисления указаны в case, то блок default можно опустить, как в примере выше. Для типов int или String блок default обязателен, иначе возникнет ошибка компиляции.
В чем отличие зарезервированного слова от ключевого слова? Отличие в том, что ключевые слова мы не можем использовать в качестве идентификаторов. Но зарезервированные слова можно использовать в качестве идентификаторов вне контекста. За пределами блока switch мы можем создать переменную с именем yield, и здесь не будет ошибки компиляции:
public static void main(String[] args) {
int month = 4;
String season = switch (month) {
case 12, 1, 2:
yield "Winter";
default:
yield "Not a Month";
};
int yield = 6;
} Но создать переменную с ключевым словом, допустим с ключевым словом case, даже за пределами блока switch нельзя — возникнет ошибка компиляции.
На заметку
Развитие switch не остановилось на Java 14. В Java 21 switch научился сопоставлению с образцом (pattern matching): в метках case можно указывать типы (case Integer i ->) и отдельную метку case null, которая позволяет обработать null без NullPointerException.
Пример сопоставления с образцом в switch (Java 21):
public class PatternMatchingExample {
public static void main(String[] args) {
Object obj = 42;
String result = switch (obj) {
case null -> "Значение null";
case Integer i when i > 0 -> "Положительное число: " + i;
case Integer i -> "Целое число: " + i;
case String s -> "Строка длиной " + s.length();
default -> "Другой тип: " + obj.getClass().getSimpleName();
};
System.out.println(result);
}
} Результат выполнения программы:
Положительное число: 42 Обратите внимание на метку case Integer i when i > 0 — это guarded pattern (метка с условием, ключевое слово when): она сработает только если объект имеет тип Integer и дополнительно проходит проверку в when. Если условие не выполняется, поиск продолжается по следующим меткам — в данном случае сработает более общая case Integer i.
3. Сравнение классического switch и выражений switch
| Характеристика | Классический switch | Выражение switch (Java 14) |
|---|---|---|
| Синтаксис метки | case 1: | case 1 -> (или case 1: с yield) |
| Несколько значений в метке | case 1: case 2: (отдельные метки) | case 1, 2 (через запятую) |
| Проваливание (fall-through) | Есть, если не указать break | Нет при стрелочном синтаксисе |
| Возврат значения | Нет, только выполнение кода | Да, через стрелку или yield |
| Блок default | Необязателен | Обязателен, кроме enum со всеми константами в case |
Часто задаваемые вопросы
Что произойдет, если передать в switch строку со значением null?
Во время выполнения будет выброшено NullPointerException — и в классическом switch, и в выражении switch. Компилятор такую ошибку не обнаруживает. Перед switch проверяйте переменную на null, а начиная с Java 21 можно использовать отдельную метку case null.
Какие типы нельзя использовать в выражении switch?
Нельзя использовать long, float, double и boolean. Разрешены char, byte, short, int, их классы-обертки, enum (начиная с Java 6) и String (начиная с Java 7). С Java 21 благодаря pattern matching в switch можно использовать и любой ссылочный тип.
Чем yield отличается от break в switch?
break просто завершает выполнение оператора switch и не возвращает значение. yield завершает выражение switch и возвращает из него результат. При этом yield — зарезервированное, а не ключевое слово: переменную с именем yield создать можно, а с именем break — нет.
Обязателен ли блок default в выражении switch?
Выражение switch должно покрывать все возможные значения. Если тип выражения enum и все его константы перечислены в case, блок default можно опустить. Для int, String и других типов default обязателен, иначе код не откомпилируется. В классическом операторе switch блок default всегда необязателен.
Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии