Почему оператор string switch не поддерживает нулевой случай?
мне просто интересно, почему Java 7 switch оператор не поддерживает a null дело и вместо этого бросает NullPointerException? См. комментируемую строку ниже (пример взят из статья Java Tutorials о switch):
{
String month = null;
switch (month) {
case "january":
monthNumber = 1;
break;
case "february":
monthNumber = 2;
break;
case "march":
monthNumber = 3;
break;
//case null:
default:
monthNumber = 0;
break;
}
return monthNumber;
}
это позволило бы избежать if условие на null проверить перед каждым switch использовать.
8 ответов:
как damryfbfnetsi указывает в комментариях JLS §14.11 имеет следующее примечание:
запрет на использование
nullкак метка переключателя предотвращает написание кода, который никогда не может быть выполнен. Еслиswitchвыражение имеет ссылочный тип, то естьStringили коробочный примитивный тип или тип перечисления, то ошибка времени выполнения произойдет, если выражение имеет значениеnullво время выполнения. в суд из дизайнеров языка программирования Java это лучший результат, чем молча пропустить весьswitchоператор или выбор для выполнения операторов (если таковые имеются) послеdefaultярлык (если таковые имеются).(выделено мной)
в то время как последнее предложение пропускает возможность использования
case null:, это кажется разумным и предлагает взгляд на намерения разработчиков языка.если мы посмотрим на детали реализации, этот блог Кристиан Hujer имеет некоторые проницательные предположения о том, почему
nullНе допускается в коммутаторах (хотя он сосредотачивается наenumпереключатель, а неStringпереключатель):под капотом
switchоператор обычно компилируется в байтовый код tablesswitch. И" физический " аргумент кswitchа также случаиints. значение int для включения определяется путем вызова методаEnum.ordinal(). Этот.[ ..] порядковые номера начинаются с нуля.это означает, что отображение
nullдо0не было бы хорошей идеей. Переключатель на первом значении перечисления будет неотличим от null. Может быть, было бы неплохо начать подсчет ординалов для перечислений в 1. Однако он не был определен так, и это определение не может быть изменено.пока
Stringкоммутаторы реализуются по-разному наenumпереключатель пришел первым и установить прецедент для того, как переключение на ссылочный тип должно вести себя, когда ссылкаnull.
это не красиво, но
String.valueOf()позволяет использовать нулевую строку в коммутаторе. Если он найдетnull, он преобразует его в"null", в противном случае он просто возвращает ту же строку, вы прошли его. Если вы не справитесь"null"явно, то он будет идти вdefault. Единственное предостережение заключается в том, что нет никакого способа отличить строку"null"иnullпеременной.String month = null; switch (String.valueOf(month)) { case "january": monthNumber = 1; break; case "february": monthNumber = 2; break; case "march": monthNumber = 3; break; case "null": monthNumber = -1; break; default: monthNumber = 0; break; } return monthNumber;
В общем
nullнеприятно обрабатывать; может быть, лучший язык может жить безnull.ваша проблема может быть решена с помощью
switch(month==null?"":month) { ... //case "": default: monthNumber = 0; }
это попытка ответить, почему он бросает
NullPointerExceptionвывод команды javap ниже показывает, что
caseвыбирается на основе хэш-кодаswitchстрока аргумента и, следовательно, бросает NPE, когда.hashCode()вызывается в нулевой строке.6: invokevirtual #18 // Method java/lang/String.hashCode:()I 9: lookupswitch { // 3 -1826660246: 44 -263893086: 56 103666243: 68 default: 95 }это означает, что на основе ответов на может ли хэш-код Java производить одно и то же значение для разных строк? хотя редко есть еще возможность совпадения двух случаев (две строки с одинаковым хэшем код) смотрите этот пример ниже
int monthNumber; String month = args[0]; switch (month) { case "Ea": monthNumber = 1; break; case "FB": monthNumber = 2; break; // case null: default: monthNumber = 0; break; } System.out.println(monthNumber);javap для которого
10: lookupswitch { // 1 2236: 28 default: 59 } 28: aload_3 29: ldc #22 // String Ea 31: invokevirtual #24 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 34: ifne 49 37: aload_3 38: ldc #28 // String FB 40: invokevirtual #24 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 43: ifne 54 46: goto 59 //Defaultну, как вы можете видеть, только один случай генерируется, но с двумя условиями if для проверки mach с каждой строкой case. Очень интересный и сложный способ реализации этой функции !
короче ... (и, надеюсь, достаточно интересно!!!)
перечисления были впервые введены в Java1.5 (Sep ' 2004) и ошибка С просьбой разрешить включение строки была подана давно (окт'95). Если вы посмотрите на комментарий, опубликованный на этой ошибке в июнь ' 2004, он скажет
Don't hold your breath. Nothing resembling this is in our plans.похоже, они отложили (игнорировать) эта ошибка и в конечном итоге запустила Java 1.5 в том же году в которой они ввели "перечисление" с порядковым номером, начинающимся с 0, и решили (пропустил) не поддерживать null для перечисления. Позже в Java1.7 (июль) они следуют (заставили) та же философия со строкой (т. е. при создании байт-кода не выполнялась проверка null перед вызовом метода hashcode ()).поэтому я думаю, что это сводится к тому, что enum пришел первым и был реализован с его порядковым номером, начинающимся с 0, из-за которого они не могли поддерживать нулевое значение в блоке коммутатора, а затем со строкой они решили принудительно использовать ту же философию, т. е. нулевое значение, не разрешенное в блоке коммутатора.
TL; DR со строкой они могли бы позаботиться о NPE (вызванном попыткой генерировать хэш-код для null) при реализации преобразования кода java в байтовый код, но, наконец, решили этого не делать.
согласно Java Docs:
коммутатор работает с примитивными данными byte, short, char и int типы. Он также работает с перечисленными типами (обсуждается в перечислимых типах), класс String и несколько специальных классов, которые обертывают определенные примитивные типы: символьный, байтовый, короткий и целочисленный (обсуждается в разделе Числа и строки).
С
nullне имеет типа и не является экземпляром ничего, он не будет работать с оператором switch.
ответ прост: если вы используете коммутатор со ссылочным типом (например, коробочный примитивный тип), Ошибка времени выполнения будет возникать, если выражение равно null, потому что распаковка будет вызывать NPE.
поэтому case null (который является незаконным) никогда не может быть выполнен в любом случае;)
Я согласен с проницательными комментариями (под капотом ....) в https://stackoverflow.com/a/18263594/1053496 в ответе @ Paul Bellora.
Я нашел еще одну причину, из моего опыта.
Если "case" может быть null, что означает, что switch(переменная) является null, то до тех пор, пока разработчик предоставляет соответствующий "нулевой" случай, мы можем утверждать, что это нормально . Но что произойдет, если разработчик не предоставит никакого совпадающего "нулевого" случая. Затем мы должны сопоставить его с случай "по умолчанию", который может быть не тем, что разработчик намеревался обрабатывать в случае по умолчанию. Поэтому сопоставление "null" по умолчанию может вызвать "удивительное поведение". Поэтому бросание "NPE"заставит разработчика явно обрабатывать все случаи. Я нашел бросание NPE в этом случае очень вдумчивым.
Comments