Перейти к основному содержимому

Заявление о переключении Java

· 8 мин. чтения

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 .

Как всегда, полный код JDK 8 и JDK 13 доступен на GitHub.