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("Сьогодні вітряно!");
     break;
  case THURSDAY:
     System.out.println("Сьогодні сонячно!");
     break;
  case WEDNESDAY:
     System.out.println("Сьогодні дощ!");
     break;
  default:
     System.out.println("Ой, щось не так!");
І enum:

public enum DayOfWeek {
  MONDAY("Monday"),
  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("Сьогодні вітряно!");
     break;
  case "Thursday":
     System.out.println("Сьогодні сонячно!");
     break;
  case "Wednesday":
     System.out.println("Сьогодні дощ!");
     break;
  default:
     System.out.println("Ой, щось не так!");
}
Незважаючи на нові можливості, підхід з використанням 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 з використанням break для повернення, компілюватися не буде((( Оператор switch в Java - 3Break буде використовуватися, але в тих ситуаціях, коли нам не потрібно нічого повертати.

Загалом

  • Використовуйте оператор case за кількості розгалужень понад два, щоб не захаращувати if-структурами код.
  • Не забувайте завершувати логічний блок кожної гілки, що відповідає конкретному значенню (блок case) викликом break.
  • Оператор switch, як вираз, крім деяких примітивних типів може використовувати також типи Enum і String.
  • Пам'ятайте про блок default – використовуйте його для обробки незапланованих значень вибору.
  • Для оптимізації продуктивності перемістіть гілки коду з варіантами вибору, які найчастіше зустрічаються, в початок блоку switch.
  • Не захоплюйтеся «оптимізацією» через видалення break наприкінці блоку вибору case – такий код складний для розуміння, і, як наслідок, його важко супроводжувати під час розвитку.
Коментарі (3)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Евгения Рівень 12
6 квітня 2024
Не розумію, чому без брейку він "збирає" всі подальші частини коду, якщо там все одно не збігається кейс? Типу, ми ж запросили саме для кейсу 3, воно б мало проскочити 4, 5 і так далі
Павло Рівень 28 Expert
20 лютого 2023
Пам'ятаю, що хтось з відомих авторів з теорії Джави писав, що використовувати switch - дуже погана ідея і все ж краще використовувати множинні else-if. А все через те, що люди схильні забувати ставити брейки і отримують незрозумілі помилки в роботі програм :)
Павло Рівень 28 Expert
20 лютого 2023
"У той же час, код, написаний на java 12 з використанням break для повернення, компілюватися не буде(((" Чи правильно я розумію, що це приклад того, що раніше написаний код (в 12 джаві) несумісний з більш новішою джава (13)? :)