1. Обзор
В этом руководстве мы узнаем, что такое оператор switch
и как его использовать.
Оператор switch
позволяет нам заменить несколько вложенных конструкций if-else
и, таким образом, улучшить читаемость нашего кода.
Switch
со временем развивался — были добавлены новые поддерживаемые типы, особенно в Java 5 и 7. Кроме того, он продолжает развиваться — выражения switch
, вероятно, будут представлены в Java 12.
Ниже мы приведем несколько примеров кода, демонстрирующих использование оператора switch
, роль оператора break
, требования к значениям аргумента/ кейса
switch
и сравнение String
в операторе switch
.
``
Перейдем к примеру.
2. Пример использования
Допустим, у нас есть следующие вложенные операторы if-else
:
public String exampleOfIF(String animal) {
String result;
if (animal.equals("DOG") || animal.equals("CAT")) {
result = "domestic animal";
} else if (animal.equals("TIGER")) {
result = "wild animal";
} else {
result = "unknown animal";
}
return result;
}
Приведенный выше код выглядит не очень хорошо, и его будет сложно поддерживать и рассуждать. Чтобы улучшить читаемость, мы могли бы использовать оператор switch
здесь:
public String exampleOfSwitch(String animal) {
String result;
switch (animal) {
case "DOG":
result = "domestic animal";
break;
case "CAT":
result = "domestic animal";
break;
case "TIGER":
result = "wild animal";
break;
default:
result = "unknown animal";
break;
}
return result;
}
Как показано выше, мы сравниваем животное
аргумента переключения
с несколькими значениями case
. Если ни одно из значений case
не равно аргументу, то выполняется блок под меткой по умолчанию .
Проще говоря, оператор break
используется для выхода из оператора switch
.
3. Заявление о перерыве
Хотя большинство операторов switch
в реальной жизни подразумевают, что должен выполняться только один из блоков case , оператор
break
необходим для выхода из switch
после завершения блока.
Если мы забудем написать break
, будут выполнены блоки под ним.
Чтобы продемонстрировать это, опустим операторы break
и добавим вывод в консоль для каждого блока:
public String forgetBreakInSwitch(String animal) {
switch (animal) {
case "DOG":
System.out.println("domestic animal");
default:
System.out.println("unknown animal");
}
}
Давайте выполним этот код забылBreakInSwitch
( «DOG»)
и проверим вывод, чтобы убедиться, что все блоки выполняются:
domestic animal
unknown animal
Поэтому мы должны быть осторожны и добавлять операторы break
в конце каждого блока, если только нет необходимости переходить к коду под следующей меткой.
Единственный блок, где разрыв
не требуется, — это последний, но добавление разрыва
в последний блок делает код менее подверженным ошибкам.
Мы также можем воспользоваться этим поведением, чтобы пропустить break
, когда мы хотим, чтобы один и тот же код выполнялся для нескольких операторов case. Давайте перепишем пример из предыдущего раздела, сгруппировав вместе первые два случая:
public String exampleOfSwitch(String animal) {
String result;
switch (animal) {
case "DOG":
case "CAT":
result = "domestic animal";
break;
case "TIGER":
result = "wild animal";
break;
default:
result = "unknown animal";
break;
}
return result;
}
4. Переключить
значения аргумента и регистра
Теперь давайте обсудим допустимые типы аргументов switch и значений
case
, требования к ним и то, как оператор switch
работает со строками.
4.1. Типы данных
Мы не можем сравнивать все типы объектов и примитивов в операторе switch
. Переключатель работает только с четырьмя примитивами и их обертками ,
а также с типом enum
и классом String :
байт
ибайт
короткий
икороткий
целое
число ицелое число
чар
ихарактер
перечисление
Нить
Строковый
тип доступен в операторе switch
, начиная с Java 7.
Тип enum
был представлен в Java 5 и с тех пор доступен в операторе switch
.
Классы-оболочки также доступны начиная с Java 5.
Конечно, значения аргумента switch и
case
должны быть одного типа.
4.2. Нет нулевых
значений
Мы не можем передать нулевое
значение в качестве аргумента оператору switch
. Если мы это сделаем, программа выдаст исключение NullPointerException,
используя наш первый пример переключения :
@Test(expected=NullPointerException.class)
public void whenSwitchAgumentIsNull_thenNullPointerException() {
String animal = null;
Assert.assertEquals("domestic animal", s.exampleOfSwitch(animal));
}
Конечно, мы также не можем передать null
в качестве значения метки case оператора
switch
. Если мы это сделаем, код не скомпилируется.
4.3. Значения case
как константы времени компиляции
Если мы попытаемся заменить значение регистра DOG
на переменную dog
, код не скомпилируется, пока мы не пометим переменную dog
как final
:
final String dog="DOG";
String cat="CAT";
switch (animal) {
case dog: //compiles
result = "domestic animal";
case cat: //does not compile
result = "feline"
}
4.4. Сравнение строк
Если оператор switch
использовал оператор равенства для сравнения строк, мы не могли правильно сравнить аргумент String ,
созданный с помощью оператора new
, со значением регистра String
.
К счастью, оператор switch использует метод
equals()
под капотом .
Давайте продемонстрируем это:
@Test
public void whenCompareStrings_thenByEqual() {
String animal = new String("DOG");
assertEquals("domestic animal", s.exampleOfSwitch(animal));
}
5. переключить
выражения
JDK 13 теперь доступен и содержит улучшенную версию новой функции, впервые представленной в JDK 12 : выражение переключения .
Чтобы включить его, нам нужно передать –enable-preview
компилятору.
5.1. Новое выражение переключателя
Давайте посмотрим, как выглядит новое выражение переключения при переключении по месяцам :
var result = switch(month) {
case JANUARY, JUNE, JULY -> 3;
case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1;
case MARCH, MAY, APRIL, AUGUST -> 2;
default -> 0;
};
Отправка значения типа Month.JUNE
установит результат
равным 3.
Обратите внимание, что в новом синтаксисе используется оператор ->
вместо двоеточия, к которому мы привыкли в операторах switch
. Кроме того, нет ключевого слова break
: выражение switch
не проходит через case
s.
Еще одним дополнением является тот факт, что теперь у нас могут быть критерии, разделенные запятыми.
5.2. Ключевое слово доходности
Идя немного дальше, есть возможность получить детальный контроль над тем, что происходит в правой части выражения, используя блоки кода. В таком случае нам нужно использовать ключевое слово yield
:
var result = switch (month) {
case JANUARY, JUNE, JULY -> 3;
case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1;
case MARCH, MAY, APRIL, AUGUST -> {
int monthLength = month.toString().length();
yield monthLength * 4;
}
default -> 0;
};
Хотя наш пример немного произволен, дело в том, что здесь мы получили доступ к большей части языка Java.
5.3. Возврат внутри переключателя
выражений
Из-за различий между операторами switch
и выражениями switch
возможен возврат
из оператора switch
, но нам не разрешено делать это из выражения switch
.
Следующий пример вполне корректен и будет компилироваться:
switch (month) {
case JANUARY, JUNE, JULY -> { return 3; }
default -> { return 0; }
}
Однако следующий код не будет скомпилирован, так как мы пытаемся вернуться
за пределы объемлющего выражения switch:
var result = switch (month) {
case JANUARY, JUNE, JULY -> { return 3; }
default -> { return 0; }
};
5.4. Исчерпанность
При использовании операторов switch
не имеет значения, охватываются ли все случаи .
Следующий код, например, абсолютно корректен и скомпилируется:
switch (month) {
case JANUARY, JUNE, JULY -> 3;
case FEBRUARY, SEPTEMBER -> 1;
}
Однако для выражений switch
компилятор настаивает на том, чтобы были охвачены все возможные случаи . Следующий фрагмент кода, например, не будет компилироваться, так как нет случая по умолчанию, и не все возможные случаи охвачены:
var result = switch (month) {
case JANUARY, JUNE, JULY -> 3;
case FEBRUARY, SEPTEMBER -> 1;
}
Однако выражение switch
будет действительным, когда будут рассмотрены все возможные случаи, как в следующем примере:
var result = switch (month) {
case JANUARY, JUNE, JULY -> 3;
case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1;
case MARCH, MAY, APRIL, AUGUST -> 2;
}
Обратите внимание, что приведенный выше фрагмент кода не имеет регистра по умолчанию
. Пока охвачены все случаи, выражение switch
будет действительным.
6. Заключение
В этом руководстве мы изучили тонкости использования оператора switch
в Java. Мы можем решить, использовать ли переключатель
на основе удобочитаемости и типа сравниваемых значений.
Оператор switch является хорошим кандидатом для случаев, когда у нас есть ограниченное количество опций в заранее определенном наборе (например, дни недели). В противном случае нам пришлось бы изменять код каждый раз, когда добавляется или удаляется новое значение, что может оказаться невыполнимым. В этих случаях мы должны рассмотреть другие подходы, такие как полиморфизм или другие шаблоны проектирования, такие как Command .