JavaRush /Java блог /Java Developer /Оператор switch в Java
Автор
Milan Vucic
Репетитор по программированию в Codementor.io

Оператор switch в Java

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

if (turn_left) { 
    System.out.println(«Коня потеряешь»); 
}
if (turn_right) {
    System.out.println(“Знания обретёшь”);
}
else 
    System.out.println(“Так и будешь стоять?”);

А что, если таких дорожек не две, а 10? Есть дорожка «совсем направо», «чуть левее», «ещё чуть-чуть левее» и так далее, в количестве 10 штук? Представьте, как разрастётся ваш if-then-else код в таком варианте!

if (вариант1)
{… }
else if (вариант2)
{…}
…
else if (вариантN) ….
Итак, у вас не одна развилка условий, а несколько, скажем, 10 (тут важно, что число развилок ограничено). Для таких ситуаций есть специальный оператор выбора — switch case java.

       switch (ВыражениеДляВыбора) {
           case  (Значение1):
               Код1;
               break;
           case (Значение2):
               Код2;
               break;
...
           case (ЗначениеN):
               КодN;
               break;
           default:
               КодВыбораПоУмолчанию;
               break;
       }

Порядок выполнения в операторе следующий:
  • Вычисляется ВыражениеДляВыбора. Далее оператор switch сравнивает полученное выражение с очередным Значением (в порядке перечисления).
  • Если ВыражениеДляВыбора совпало со Значением, то выполняется код, идущий после двоеточия.
  • Если встречается конструкция break — то управление передается за пределы команды switch.
  • Если совпадений ВыражениеДляВыбора и Значений не выявлено, то управление передаётся КодуВыбораПоУмолчанию.
Важные моменты
  • Тип ВыражениеДляВыбора для оператора выбора switch в Java должен быть одним из следующих:

    • byte, short, char, int.
    • Их обёртки Byte, Short, Character, Integer.
    • String (начиная с Java 7).
    • Перечисление (Enum).
  • блок default — необязательный, тогда в случае отсутствия совпадений ВыраженияДляВыбора и Значений не будет выполнено никаких действий.
  • break не является обязательным, если его нет – код продолжит выполнение (игнорируя дальнейшие сравнения значений в блоках case) до первого встреченного break или до конца оператора switch.
  • если необходимо выполнять один и тот же код для нескольких вариантов выбора, для исключения дублирования перед ним указываем несколько соответствующих значений в подряд идущих блоках case.

Перейдем к практике использования оператора switch в Java

Не волнуйтесь, с теорией покончили, и, после дальнейших примеров станет всё гораздо понятнее. Итак, приступим. Давайте рассмотрим пример из астрономии о планетах Солнечной системы. В соответствии с последними международными положениями исключим Плутон (за свойства ее орбиты). Вспомним, что планеты у нас расположены от Солнца в следующей последовательности: Меркурий, Венера, Земля, Марс, Юпитер, Сатурн, Уран и Нептун. Создадим Java метод, который получает на вход порядковый номер планеты (относительно удалённости от Солнца), а на выходе выдает основной состав атмосферы этой планеты в виде списка List<String>. Напомню, у некоторых планет – схожий состав атмосферы. Так, Венера и Марс содержат в основном углекислый газ, у Юпитера и Сатурна она состоят из водорода и гелия, а Уран и Нептун вдобавок к последней паре газов также имеет метан. Наша функция:

public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
    List<String> result = new ArrayList<>();
    switch (seqNumberFromSun) {
        case 1: result.add("Нет атмосферы");
            break;
        case 2:
        case 4: result.add("Углекислый газ");
            break;
        case 3: result.add("Углекислый газ");
            result.add("Азот");
            result.add("Кислород");
            break;
        case 5:
        case 6: result.add("Водород");
            result.add("Гелий");
            break;
        case 7:
        case 8: result.add("Метан");
            result.add("Водород");
            result.add("Гелий");
            break;
        default:
            break;
    }
    return result;
}
Обратите внимание: идентичным по составу атмосфер планетам мы сопоставили один и тот же код. А сделали мы это за счёт подряд идущих конструкций case. Итого, если мы хотим получить состав атмосферы нашей с вами родной планеты, вызываем наш метод с параметром 3:

getPlanetAtmosphere(3).
System.out.println(getPlanetAtmosphere(3)) вернет нам [Углекислый газ, Азот, Кислород].
Эксперимент с break Что получится, если мы уберем все операторы break? Попробуем на практике:

    public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
        List<String> result = new ArrayList<>();
        switch (seqNumberFromSun) {
            case 1: result.add("Нет атмосферы");
            case 2:
            case 4: result.add("Углекислый газ");
            case 3: result.add("Углекислый газ");
                result.add("Азот");
                result.add("Кислород");
            case 5:
            case 6: result.add("Водород");
                result.add("Гелий");
            case 7:
            case 8: result.add("Метан");
                result.add("Водород");
                result.add("Гелий");
            default:
        }
        return result;
    }
Если мы распечатаем результат работы метода System.out.println(getPlanetAtmosphere(3)), то наша родная планета окажется не такой уж пригодной для жизни. Или пригодной? Судите сами: [Углекислый газ, Азот, Кислород, Водород, Гелий, Метан, Водород, Гелий], Почему так получилось? Программа выполнила все case после первого совпадения и до конца блока switch.

Излишняя оптимизация break

Заметим, что мы можем усовершенствовать метод иным расположением директив break и вариантов выбора

public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
    List<String> result = new ArrayList<>();
    switch (seqNumberFromSun) {
        case 1: result.add("Нет атмосферы");
                break;
        case 3: result.add("Азот");
                result.add("Кислород");
        case 2:
        case 4: result.add("Углекислый газ");
                break;
        case 7:
        case 8: result.add("Метан");
        case 5:
        case 6: result.add("Водород");
                result.add("Гелий");
    }
     return result;
}
Выглядит короче, не так ли? Мы сократили общее число операторов, поигравшись с порядком следования case и перегруппировкой. Теперь каждый вид газа добавляется в список только в одной строчке кода. Листинг последнего примера метода показан только для демонстрации работы, крайне не рекомендуется писать в подобном стиле. Если автору (а тем более сторонним программистам) схожего кода предстоит его сопровождать, то будет весьма тяжело восстановить логику формирования блоков выбора и выполняемого кода для оператора switch java.

Отличия от if

При внешней схожести операторов if и switch не забывайте, что выбор вариантов выполнения оператор множественного выбора switch основывает на КОНКРЕТНОМ ЗНАЧЕНИИ, тогда как в if. может быть любое логическое выражение. Учитывайте данный факт, проектируя ваш код. Давайте немного подробнее рассмотрим нововведения для switch в разных версиях Java.

Switch в Java 7

До Java 7 в качестве значения для переключателя можно было использовать byte, short, char и int примитивы. Также была поддержка enum и оберток перечисленных выше примитивных типов: Character, Byte, Short, и Integer. Но ведь часто нам нужно найти значение java switch string! Как бы это выглядело в Java 6:

DayOfWeek day = DayOfWeek.fromValue("Thursday");

switch (day) {
  case MONDAY:
     System.out.println("Today is windy !");
     break;
  case THURSDAY:
     System.out.println("Today is sunny !");
     break;
  case WEDNESDAY:
     System.out.println("Today is rainy!");
     break;
  default:
     System.out.println("Oooops, something wrong !");
И enum:

public enum DayOfWeek {
  MONDAY("Monday"),
  THURSDAY("Thursday"),
  WEDNESDAY("Wednesday"),
  NOT_FOUND("Not found");
 
  private final String value;
 
  DayOfWeek(final String value) {
     this.value = value;
  }
 
  public static DayOfWeek fromValue(String value) {
     for (final DayOfWeek dayOfWeek : values()) {
        if (dayOfWeek.value.equalsIgnoreCase(value)) {
           return dayOfWeek;
        }
     }
     return NOT_FOUND;
  }
}
Но начиная с Java 7 добавилась возможно использовать тип String как значение для переключателя switch:

String day = "Thursday";

switch (day) {
  case "Monday":
     System.out.println("Today is windy !");
     break;
  case "Thursday":
     System.out.println("Today is sunny !");
     break;
  case "Wednesday":
     System.out.println("Today is rainy!");
     break;
  default:
     System.out.println("Oooops, something wrong !");
}
Несмотря на новые возможности, подход с использованием enum более гибок и рекомендуем к использованию: этот enum мы можем переиспользовать много раз.

Switch в Java 12

В Java 12 были улучшены выражения Switch для сопоставления с образцом. Если использовать Switch как и в примере выше, для задания значения некоторой переменной мы должны были вычислить значение и присвоить заданной переменной, а затем использовать break:

int count = 2;
int value;
switch (count) {
  case 1:
     value = 12;
     break;
  case 2:
     value = 32;
     break;
  case 3:
     value = 52;
     break;
  default:
     value = 0;
}
Но благодаря возможностям 12-й версии Java мы можем переписать данное выражение следующим образом:

int value = switch (count) {
  case 1:
     break 12;
  case 2:
     break 32;
  case 3:
     break 52;
  default:
     break 0;
};
Давайте немного пройдёмся по измененным моментам:
  1. Если ранее мы задавали переменной значение внутри блоков case, так как сам оператор switch не мог ничего возвращать, сейчас такая возможность у нас есть, и мы непосредственно с помощью switch возвращаем значение.

  2. Ранее справа от break у нас уже ничего не могло стоять, а сейчас мы его используем как оператор return для возвращения значения нашим switch. Метки с двоеточием отмечают точку входа в блок операторов. То есть с того места начинается выполнение всего кода ниже, даже тогда, когда встречается другая метка.

    Как итог — сквозной переход от метки к метке, который еще называют проваливанием (fall-through).

Оператор switch в Java - 2Для завершения такого прохода нужно либо полностью пройтись по всем элементам, либо использовать break или return. Нововведение в java 12 даёт нам возможность использовать лямбда оператор, который в свою очередь гарантирует , что будет выполнен только код справа от нее. Без всякого "проваливания". Как будем выглядеть предыдущий пример в таком случае:

int count = 2;
int value = switch (count) {
  case 1 -> 12;
  case 2 -> 32;
  case 3 -> 52;
  default -> 0;
};
Код стал значительно проще, не правда ли? И ещё: лямбда оператор может служить и типичным аналогом двоеточия, после которого идёт целый блок с некоторыми операциями:

int count = 2;
int value = switch (count) {
  case 1 -> {
     //некоторые вычислительные операции...
     break 12;
  }
  case 2 -> {
     //некоторые вычислительные операции...
     break 32;
  } 
  case 3 -> {
     //некоторые вычислительные операции...
     break 52;
  }
  default -> {
     //некоторые вычислительные операции...
     break 0;
  } 
};
Ну а что если у нас для некоторых кейсов возвращаемое значение будет одинаковым? Получится, что у нас фактически одинаковые кейсы для некоторых разных значений. Как бы это можно было бы сократить с помощью новых возможностей Java 12:

int count = 2;
int value = switch (count) {
  case 1, 3, 5 -> 12;
  case 2, 4, 6 -> 52;
  default -> 0;
};

Switch в Java 13

В Java 13 изменился способ возврата значения из switch. Если в java 12 возвращаемое значение мы писали после break, который служил у нас как return для блока switch, то сейчас возвращать значение мы будем с помощью слова yield. Смотрим:

int value = switch (count) {
  case 1:
     yield 12;
  case 2:
     yield 32;
  case 3:
     yield 52;
  default:
     yield 0;
};
В тоже время, код, написанный на java 12 c использованием break для возврата, компилироваться не будет(( Оператор switch в Java - 3Break будет использоваться, но в тех ситуациях, когда нам не нужно ничего возвращать.

Итого

  • Используйте оператор case при числе ветвлений более двух, чтобы не загромождать if-структурами код.
  • Не забывайте завершать логический блок каждой ветки соответствующего конкретному значению (блок case) вызовом break.
  • Оператор switch помимо некоторых примитивных типов, в качестве выражения может использовать также типы Enum и String.
  • Помните про блок default – употребляйте его для обработки незапланированных значений выбора.
  • Для оптимизации производительности переместите ветки кода с наиболее часто встречающимися вариантами выбора к началу блока switch.
  • Не увлекайтесь «оптимизацией» за счёт удаления break в конце блока выбора case – такой код сложен для понимания, и, как следствие, тяжело сопровождать при его развитии.
Комментарии (109)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Максим Li Уровень 36
4 декабря 2023
Узнал много нового!
Кру Уровень 22
25 ноября 2023
я Ваш фанат! статьи такие понятные и информативные! спасибо!
Private Joker Уровень 24
31 октября 2023
А про самый удобный варик записи, я что-то не увидел switch (random) { case 0: return CERTAIN; case 1: return DEFINITELY; case 2: return MOST_LIKELY; case 3:
Jumper Уровень 23
13 октября 2023
Не могу понять людей которые статью не поняли. Всё предельно разжёвано. Просто попробуйте как оно работает и всё поймёте.
Дария Уровень 10 Expert
2 октября 2023
и тут ты понимаешь, что тебе нужен репетитор
Gregory2.0 Уровень 20
14 сентября 2023
Такой вопрос, обязательно ли в этом коде break int count = 2; int value = switch (count) { case 1 -> { //некоторые вычислительные операции... break 12; } case 2 -> { //некоторые вычислительные операции... break 32; } Или можно поступить каr здесь: int count = 2; int value = switch (count) { case 1 -> 12; case 2 -> 32; case 3 -> 52; default -> 0; };
komandor1 Уровень 27
4 сентября 2023
То ли я тупой, то ли лыжи не едут), но я не понял зачем нужны пустые кейсы, а потом перечитал раз 10 и кааак опять не понял
Dmitry Vidonov Уровень 29 Expert
14 августа 2023
Круто! Очень много интересного по свитчу
Сергей Уровень 24 Expert
30 июля 2023
подскажите, пожалуйста, не могу до конца понять чем отличается несколько if подряд от if и else if ?
Alexander Rozenberg Уровень 32
18 июля 2023
fine