Фото від Museums Victoria на Unsplash
Хоча це, можливо, не так травматично, як виключення через null pointer, але проблема fall-through в Switch-Case завдала чимало головного болю багатьом програмістам на Java. Хоча в деяких випадках fall-through може бути зручним, найчастіше це призводить до несподіваних проблем.
Як приклад, розглянемо маленьку програму, яка повинна вивести сьогоднішню дату і кількість днів у поточному місяці.
LocalDate today = LocalDate.now();
System.out.println("Today is " + today.toString());
Month month = today.getMonth();
int numberOfDays = 0;
switch (month) {
case JANUARY:
case MARCH:
case MAY:
case JULY:
case AUGUST:
case OCTOBER:
case DECEMBER:
numberOfDays = 31;
case FEBRUARY:
if (today.isLeapYear()) {
numberOfDays = 29;
} else {
numberOfDays = 28;
}
case APRIL:
case JUNE:
case SEPTEMBER:
case NOVEMBER:
numberOfDays = 30;
}
System.out.println("The month "
+ month.getDisplayName(TextStyle.FULL,
Locale.US) + " has "
+ numberOfDays + " days");
Виведення буде помилковим більше половини року.
Today is 2025–01–03
The month January has 30 days
Зрозуміло, що це неправильно. Січень має 31 день, як і сказано в Old Farmer’s Almanac. Це не велика проблема в тестовому прикладі. Однак не важко уявити, як fall-through в Switch-Case може спричинити проблеми в реальних програмах.
Ваше середовище розробки (IDE), наприклад Apache NetBeans, ймовірно попереджає вас про не використані присвоєння.
Якщо ми просто додамо Break
після рядків з попередженнями, NetBeans усуне ці попередження, і наша програма повинна вивести правильний результат.
Today is 2025–01–03
The month January has 31 days
Але якщо ваша IDE використовує Java 12 або 13 з увімкненими попередніми функціями, або Java 14 або пізніші версії, то ви можете отримати попередження на рядку з “switch (month)
”. У NetBeans це попередження вказує на можливість використати "rule switch". Інші IDE можуть сказати, що ви можете використати "switch expression".
Switch expression були додані як попередня функція в Java 12, залишалися в попередньому режимі в Java 13 і були офіційно прийняті в Java 14.
NetBeans переписав Switch-Case вище, як показано нижче:
switch (month) {
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER,
DECEMBER -> numberOfDays = 31;
case FEBRUARY -> {
if (today.isLeapYear()) {
numberOfDays = 29;
} else {
numberOfDays = 28;
}
}
case APRIL, JUNE, SEPTEMBER, NOVEMBER ->
numberOfDays = 30;
}
Але ми можемо зробити краще. Ми можемо написати щось, що майже нагадує Match-Case в Scala.
int numberOfDays = switch (month) {
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER,
DECEMBER -> 31;
case FEBRUARY -> {
if (today.isLeapYear()) {
yield 29;
} else {
yield 28;
}
}
case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
};
Зверніть увагу на використання фігурних дужок і зарезервоване слово yield
в випадку лютого.
Switch expression повинні бути вичерпними. Якби ми використовували 32-бітні цілі числа замість типу enum Month
, нам довелося б надати випадок Default
(використання типу enum також позбавляє нас турбот про те, чи є січень 0 чи 1).
Багато хто з вас, хто читає це, ймовірно, все ще використовує Java 8 у своїй професійній роботі через вимоги клієнтів. Але для ваших особистих проєктів, можливо, варто оновитися до новішої версії Java і скористатися можливостями, як от Switch expression.
Перекладено з: Switch expressions in Java 14 and later