В предыдущей статье я написал про некоторые новшества в Java 7
(входящие в Project Coin) а именно:
– Бриллиантовый оператор (Improved type inference for generic instance creation diamond)
– Автоматически закрывающиеся ресурсы (try-with-resource).
Конечно, это очень приятные вещи, но больше всего я рад появлению Strings in switch!
Возможность использовать строки в switch люди ждут уже где-то около 16 лет.
Например, вот ссылка на запрос по добавлению такой фичи:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179.
Лично мне очень приятно, что наконец-то такая возможность появилось. Примеры использования можно посмотреть на официальной страничке Oracle учебника по Java (уже обновленного) .
Пример из учебника:
// http://download.oracle.com/javase/tutorial/java/nutsandbolts/switch.html
public static int getMonthNumber(String month) {
int monthNumber = 0;
if (month == null) { return monthNumber; }
switch (month.toLowerCase()) {
case "january": monthNumber = 1; break;
case "february": monthNumber = 2; break;
case "march": monthNumber = 3; break;
case "april": monthNumber = 4; break;
case "may": monthNumber = 5; break;
case "june": monthNumber = 6; break;
case "july": monthNumber = 7; break;
case "august": monthNumber = 8; break;
case "september": monthNumber = 9; break;
case "october": monthNumber = 10; break;
case "november": monthNumber = 11; break;
case "december": monthNumber = 12; break;
default: monthNumber = 0; break;
}
return monthNumber;
}
Стоит сразу отметить в этом примере проверку на null. Дело в том, что если в switch нам попадет ссылка на null, то возникнет NullPointerException.
Для того, чтобы понять почему это происходит, нужно посмотреть в какой код преобразуется switch . На самом деле switch от строк преобразуется в 2 (два!) switch-а.
Например, возьмем такой код:
private static void switchCheck(String s) {
switch (s) {
case "a":
System.out.println("a");
case "abcd":
System.out.println("abcd");
case "abdE":
System.out.println("abdE");
default:
System.out.println("default");
}
}
Чтобы не мучиться с javap, можно взять какой-нибудь старенький java decompiler (например JAD) и попробовать посмотреть, что получилось:
private static void switchCheck(String s) {
String s1 = s;
byte byte0 = -1;
switch (s1.hashCode()) {
case 97:
if (s1.equals("a")) {
byte0 = 0;
}
break;
case 2987074:
if (s1.equals("abdE")) {
byte0 = 2;
} else if (s1.equals("abcd")) {
byte0 = 1;
}
break;
}
switch (byte0) {
case 0:
System.out.println("a");
case 1:
System.out.println("abcd");
case 2:
System.out.println("abdE");
default:
System.out.println("default");
break;
}
}
В принципе алгоритм напоминает устройство обычных хэш-таблиц.
Первым делом, вычисляем хэш. Именно поэтому, если в switch нам попадет ссылка на null, то возникнет NullPointerException.
Затем, если есть коллизии (например у строки “abcd” и “abdE” одинаковый хэш-код), то используем следующий свободный номер.
Далее перескакиваем в новом switch-е на новый адрес. Получается довольно эффективный алгоритм, который будет работать лучше чем цепочка из if () {} else if () {} else {}.