«1. Обзор

В этом руководстве мы узнаем, что такое оператор switch и как его использовать.

Оператор switch позволяет нам заменить несколько вложенных конструкций if-else и, таким образом, улучшить читабельность нашего кода.

Переключатель развивался с течением времени — были добавлены новые поддерживаемые типы, особенно в Java 5 и 7. Кроме того, он продолжает развиваться — выражения переключателя, вероятно, будут представлены в Java 12.

Ниже мы Я приведу несколько примеров кода, чтобы продемонстрировать использование оператора switch, роль оператора break, требования к значениям аргумента/регистра switch и сравнение строк в операторе 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;
}

Как показано выше, мы сравниваем животное аргумента switch с несколькими значениями case. Если ни одно из значений case не равно аргументу, то выполняется блок под меткой по умолчанию.

Проще говоря, оператор break используется для выхода из оператора switch.

3. Оператор break

Хотя большинство операторов switch в реальной жизни подразумевают, что должен выполняться только один из блоков case, оператор break необходим для выхода из switch после завершения блока.

Если мы забудем написать разрыв, будут выполнены блоки под ним.

Чтобы продемонстрировать это, давайте опустим операторы break и добавим выходные данные в консоль для каждого блока: получить выполнение:

public String forgetBreakInSwitch(String animal) {
    switch (animal) {
    case "DOG":
        System.out.println("domestic animal");
    default:
        System.out.println("unknown animal");
    }
}

Так что мы должны быть осторожны и добавлять операторы break в конце каждого блока, если только нет необходимости переходить к коду под следующей меткой.

domestic animal
unknown animal

Единственный блок, где разрыв не нужен, это последний, но добавление разрыва в последний блок делает код менее подверженным ошибкам.

Мы также можем воспользоваться этим поведением, чтобы пропустить break, когда мы хотим, чтобы один и тот же код выполнялся для нескольких операторов case. Давайте перепишем пример из предыдущего раздела, сгруппировав вместе первые 2 случая:

4. Значения аргумента и случая переключателя

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;
}

Теперь давайте обсудим допустимые типы значений аргумента и случая переключателя, требования к ним и как оператор switch работает со строками.

4.1. Типы данных

Мы не можем сравнивать все типы объектов и примитивов в операторе switch. Переключатель работает только с четырьмя примитивами и их обертками, а также с типом enum и классом String:

byte и Byte short и Short int и Integer char и Character enum String

    Тип String доступен в переключателе начиная с Java 7. Тип перечисления

был введен в Java 5 и с тех пор доступен в операторе switch.

Классы-оболочки также были доступны, начиная с Java 5.

Конечно, аргументы переключения и значения case должны быть одного типа.

4.2. Нет нулевых значений

Мы не можем передать нулевое значение в качестве аргумента оператору switch. Если мы это сделаем, программа выдаст исключение NullPointerException, используя наш первый пример с переключателем:

Конечно, мы не можем также передать null в качестве значения метке case оператора switch. Если мы это сделаем, код не скомпилируется.

@Test(expected=NullPointerException.class)
public void whenSwitchAgumentIsNull_thenNullPointerException() {
    String animal = null;
    Assert.assertEquals("domestic animal", s.exampleOfSwitch(animal));
}

4.3. Значения case как константы времени компиляции

Если мы попытаемся заменить значение case DOG на переменную dog, код не скомпилируется, пока мы не пометим переменную dog как final:

4.4. Сравнение строк

final String dog="DOG";
String cat="CAT";

switch (animal) {
case dog: //compiles
    result = "domestic animal";
case cat: //does not compile
    result = "feline"
}

Если оператор switch использовал оператор равенства для сравнения строк, мы не могли правильно сравнить аргумент String, созданный с помощью оператора new, со значением регистра String.

К счастью, оператор switch использует метод equals() под капотом.

Давайте продемонстрируем это:

5. Выражения switch

@Test
public void whenCompareStrings_thenByEqual() {
    String animal = new String("DOG");
    assertEquals("domestic animal", s.exampleOfSwitch(animal));
}

JDK 13 теперь доступен и содержит улучшенную версию новой функции, впервые представленной в JDK 12: выражение switch.

«Чтобы включить его, нам нужно передать компилятору enable-preview.

5.1. Новое выражение переключателя

Давайте посмотрим, как выглядит новое выражение переключателя при переключении между месяцами:

Отправка значения типа Month.JUNE установит результат равным 3.

var result = switch(month) {
    case JANUARY, JUNE, JULY -> 3;
    case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1;
    case MARCH, MAY, APRIL, AUGUST -> 2;
    default -> 0; 
};

Обратите внимание, что новый синтаксис использует оператор -\u003e вместо двоеточия, к которому мы привыкли в операторах switch. Кроме того, отсутствует ключевое слово break: выражение switch не проходит через регистры.

Еще одним дополнением является тот факт, что теперь мы можем иметь критерии, разделенные запятыми.

5.2. Ключевое слово yield

Идя немного дальше, есть возможность получить детальный контроль над тем, что происходит в правой части выражения, используя блоки кода. В таком случае нам нужно использовать ключевое слово yield:

Хотя наш пример немного произволен, дело в том, что здесь мы получили доступ к большему количеству языка Java.

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;
};

5.3. Возврат внутри выражений переключателя

Вследствие различия между операторами переключателя и выражениями переключателя возможен возврат из оператора переключателя, но нам не разрешено делать это из выражения переключателя.

Следующий пример вполне корректен и будет компилироваться:

Однако следующий код не будет компилироваться, так как мы пытаемся выполнить возврат вне объемлющего выражения switch:

switch (month) {
    case JANUARY, JUNE, JULY -> { return 3; }
    default -> { return 0; }
}

5.4. Полнота

var result = switch (month) {
    case JANUARY, JUNE, JULY -> { return 3; }
    default -> { return 0; }
};

При использовании операторов switch не имеет большого значения, охватываются ли все случаи.

Следующий код, например, абсолютно корректен и компилируется:

Однако для выражений 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;
}

6. Заключение

var result = switch (month) {
    case JANUARY, JUNE, JULY -> 3;
    case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1;
    case MARCH, MAY, APRIL, AUGUST -> 2;
}

В этом уроке мы изучили тонкости использования оператора switch в Java. Мы можем решить, использовать ли переключатель, основываясь на удобочитаемости и типе сравниваемых значений.

Оператор switch хорошо подходит для случаев, когда у нас есть ограниченное количество опций в заранее определенном наборе (например, дни недели). В противном случае нам пришлось бы изменять код каждый раз, когда добавляется или удаляется новое значение, что может оказаться невыполнимым. В этих случаях мы должны рассмотреть другие подходы, такие как полиморфизм или другие шаблоны проектирования, такие как Command.

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

«

As always the complete JDK 8 code and JDK 13 code is available on GitHub.