Почему оператор 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 использовать.

558   8  

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 в байтовый код, но, наконец, решили этого не делать.

Ref: TheBUG, JavaVersionHistory, JavaCodeToByteCode, так

согласно 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

    Ничего не найдено.