Оператор switch в Java

🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!

Немного теории о switch в Java

Представьте себе рыцаря, остановившегося на развилке дороги. Пойдёшь налево — потеряешь коня. Пойдёшь направо — получишь знания. Как это описать в коде? Вы, вероятно, уже знаете, что для этого используются конструкции типа if-then и if-then-else .

if (turn_left) {
    System.out.println("You will lose your horse");
}
if (turn_right) {
    System.out.println("You will gain knowledge");
}
else
    System.out.println("So you're just going to stand there?");
Run Code

Но что, если дорог не две, а десять? Например, есть дороги, которые находятся “полностью справа”, “немного левее этого”, “еще немного левее” и так 10 возможных направлений?

Только представьте, во что превратится код с "if-then-else"!

if (option1)
{…}
else if (option2)
{…}

else if (optionN) ...
Run Code

Предположим, есть развилка на 10 дорог (важно: количество вариантов должно быть конечным). Для таких ситуаций в Java есть оператор switch.

switch (ExpressionForMakingAChoice) {
           case (Value1):
               Code1;
               break;
           case (Value2):
               Code2;
               break;
...
           case (ValueN):
               CodeN;
               break;
           default:
               CodeForDefaultChoice;
               break;
       }
Run Code

Вот как это работает:

  • Вычисляется выражение ForMakingAChoice. Затем оператор switch сравнивает полученное значение с ValueX (в том порядке, в котором они перечислены).
  • Если ExpressionForMakingAChoice совпадает с ValueX, то выполняется код, следующий за двоеточием.
  • Если встречается оператор break, то происходит выход из switch.
  • Если ExpressionForMakingAChoice не соответствует ни одному ValueX, то срабатывает CodeForDefaultCase.

Важные моменты

  • В операторе switch тип выражения ExpressionForMakingAChoice должен быть одним из следующих:
    • byteshortcharint.
    • ByteShortCharacterInteger.
    • String.
    • Enum.
  • Блок default является необязательным. Если он отсутствует и выражение ForMakingAChoice не совпадает ни с одним ValueX, то никакое действие не будет выполнено.
  • Оператор break также не обязателен. Если он отсутствует, код будет продолжать выполняться (игнорируя дальнейшие сравнения в операторах case) до первого появления break или до конца оператора switch.
  • Если один и тот же код должен быть выполнен для нескольких вариантов, можно устранить дублирование, указав несколько идущих подряд case.

Теперь посмотрим, как используется оператор switch в Java

Не волнуйтесь: мы закончили с теорией. После того как вы увидите следующие примеры, все станет гораздо понятнее. Рассмотрим пример из астрономии, связанный с планетами нашей Солнечной системы.

В соответствии с последними международными договоренностями, мы исключили Плутон (из-за особенностей его орбиты). Напомним, что наши планеты расположены по расстоянию от Солнца следующим образом: Меркурий, Венера, Земля, Марс, Юпитер, Сатурн, Уран и Нептун.

Давайте напишем метод, который принимает порядковый номер планеты (относительно ее расстояния от Солнца) и возвращает основные компоненты ее атмосферы в виде List<String>.

Напомним, что у некоторых планет состав атмосферы совпадает. Так, Венера и Марс содержат в основном углекислый газ, атмосфера Юпитера и Сатурна состоит из водорода и гелия, а Уран и Нептун добавляют к последней паре газов метан.

Вот наш метод:

public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
    List<String> result = new ArrayList<>();
    switch (seqNumberFromSun) {
        case 1: result.add("No atmosphere");
            break;
        case 2:
        case 4: result.add("Carbon dioxide");
            break;
        case 3: result.add("Carbon dioxide");
            result.add("Nitrogen");
            result.add ("Oxygen");
            break;
        case 5:
        case 6: result.add("Hydrogen");
            result.add("Helium");
            break;
        case 7:
        case 8: result.add("Methane");
            result.add("Hydrogen");
            result.add("Helium");
            break;
        default:
            break;
    }
    return result;
}
Run Code

Обратите внимание, что для планет с одинаковым составом атмосферы используется один и тот же код — просто перечислены несколько case подряд.

Если мы хотим получить состав атмосферы нашей родной планеты, мы вызываем наш метод с 3 в качестве аргумента:

getPlanetAtmosphere(3).
System.out.println(getPlanetAtmosphere(3)) returns ["Carbon dioxide", "Nitrogen", "Oxygen"].
Run Code

Эксперимент с break:

Что произойдет, если убрать все операторы break? Проверим:

public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
    List<String> result = new ArrayList<>();
    switch (seqNumberFromSun) {
        case 1: result.add("No atmosphere");
        case 2:
        case 4: result.add("Carbon dioxide");
        case 3: result.add("Carbon dioxide");
            result.add("Nitrogen");
            result.add ("Oxygen");
        case 5:
        case 6: result.add("Hydrogen");
            result.add("Helium");
        case 7:
        case 8: result.add("Methane");
            result.add("Hydrogen");
            result.add("Helium");
        default:
    }
    return result;
}
Run Code

Если вывести результат System.out.println(getPlanetAtmosphere(3)), то окажется, что наша родная планета не так уж и пригодна для жизни. Или нет? Смотрите сами:

["Carbon dioxide", "Nitrogen", "Oxygen", "Hydrogen", "Helium", "Methane", "Hydrogen", "Helium"].

Почему это произошло? Потому что программа выполняет все case начиная с первого совпадения и до конца блока switch.

Чрезмерная оптимизация с использованием break

Обратите внимание, что можно улучшить этот метод, по-другому расположив операторы break и case:

public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
    List<String> result = new ArrayList<>();
    switch (seqNumberFromSun) {
        case 1: result.add("No atmosphere");
                break;
        case 3: result.add("Nitrogen");
                result.add ("Oxygen");
        case 2:
        case 4: result.add("Carbon dioxide");
                break;
        case 7:
        case 8: result.add("Methane");
        case 5:
        case 6: result.add("Hydrogen");
                result.add("Helium");
    }
     return result;
}
Run Code

Выглядит компактнее, правда? Мы сократили количество операторов, изменив порядок case и объединив их. Теперь каждый тип газа добавляется в список всего одной строкой.

Код, приведенный в последнем примере, лишь показывает, как все работает. Мы не рекомендуем писать код таким образом. Поддержка такого кода (особенно другим разработчиком) превратится в головоломку: логику построения этих блоков будет крайне трудно восстановить.

Отличия от if

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

Ограничения для меток case: что можно и что нельзя использовать

Сначала разберемся с метками case. Возможно, вы думаете: “Могу ли я использовать здесь любое выражение?” Не совсем. В Java существуют строгие правила относительно того, что можно поместить после ключевого слова case.

Золотое правило: метки case должны быть константными выражениями. Это значит, что их значение должно быть известно компилятору уже на этапе компиляции. Никаких переменных!

Пример некорректной метки case:

int number = 2;
switch (number) {
    case number:  // Error! 'number' is a variable, not a constant.
        System.out.println("Invalid case label.");
        break;
}
Run Code

Пример корректной метки case:

final int CONST_NUMBER = 2;  // 'final' makes this a constant.
switch (CONST_NUMBER) {
    case 2:
        System.out.println("This is valid!");
        break;
}
Run Code

Основные выводы:

  • метки case должны быть константными выражениями (например, final-переменные, литералы или константы enum);
  • нельзя использовать переменные или выражения, которые могут изменяться во время выполнения.

Визуализация switch-case с помощью блок-схемы

Иногда картинка действительно стоит тысячи слов. Давайте посмотрим, как работает оператор switch-case, используя блок-схему. Это поможет понять, как именно проходит поток управления между разными ветками.

Разбираем блок-схему

Теперь давайте разберемся в этой схеме. Вот как она работает шаг за шагом:

  1. Start: программа начинает выполняться.
  2. Evaluate Expression: значение в операторе switch вычисляется.
  3. Compare Cases: программа последовательно проверяет каждую метку case.
  4. Match Found: если совпадение найдено, выполняется соответствующий блок кода.
  5. Break: если есть оператор break, программа выходит из switch. Если нет, она продолжает выполняться (это называется fall-through).
  6. Default: если соответствие не найдено, запускается блок default (если он задан).
  7. End: программа продолжает работу после блока switch.

Не так уж и страшно, если все разложить по полочкам!

Зачем использовать блок-схемы для понимания логики switch-case?

Теперь вы, возможно, спросите: “Зачем нужны блок-схемы?”. Отличный вопрос!

  • Наглядность: блок-схемы дают возможность увидеть в общих чертах, как принимаются решения в вашей программе.
  • Облегчение отладки: они помогают понять, где именно может быть ошибка в логике, особенно в сложных условиях.
  • Ускоренное обучение: для тех, кто лучше воспринимает информацию визуально, схема ускоряет понимание.
  • Удобная документация: блок-схемы помогают объяснить код коллегам или будущим пользователям.

Пример работы switch-case

Рассмотрим теперь это все на практике!

Пример: выбор уровня игры

import java.util.Scanner;

public class GameLevel {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Select your level: 1 (Easy), 2 (Medium), 3 (Hard)");
        int level = scanner.nextInt();

        switch (level) {
            case 1:
                System.out.println("You've chosen Easy mode. Good luck!");
                break;
            case 2:
                System.out.println("You've chosen Medium mode. Let's go!");
                break;
            case 3:
                System.out.println("You've chosen Hard mode. Brace yourself!");
                break;
            default:
                System.out.println("Invalid choice. Please select 1, 2, or 3.");
        }
    }
}
Run Code

В этом примере программа проверяет ввод пользователя и выполняет соответствующий блок кода. Оператор break гарантирует, что выполнится только один блок, предотвращая падение к следующему случаю!

Заключение

  • Используйте оператор switch-case, если необходимо обработать более двух ветвей, чтобы не перегружать код многочисленными if.
  • Не забудьте завершить логический блок для каждого конкретного значения (оператор case), вставив оператор break.
  • В выражении switch можно использовать не только примитивные типы, но и Enum, а также String.
  • Помните про блок default — он нужен для обработки непредвиденных значений.
  • Для оптимизации производительности поместите наиболее часто используемые ветви в начало блока switch.
  • Не увлекайтесь “оптимизацией”, удаляя break — такой код становится трудночитаемым и сложным для поддержки.

Перевод статьи «Java Switch Statement».

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх