java значений Почему оператор String не поддерживает нулевой регистр?



switch case java несколько значений (7)

Ответ просто заключается в том, что если вы используете переключатель с ссылочным типом (например, тип примитивного типа), ошибка выполнения будет выполняться, если выражение равно нулю, потому что при распаковке он будет бросать NPE.

поэтому случай null (который является незаконным) никогда не может быть выполнен в любом случае;)

Мне просто интересно, почему оператор switch 7 Java не поддерживает null регистр и вместо этого генерирует NullPointerException ? См. Прокомментированную строку ниже (пример, взятый из статьи Java Tutorials on 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;
}

Это позволило бы избежать условия для нулевой проверки перед использованием каждого switch .


Answer #1

Длинная история короткая ... (и, надеюсь, достаточно интересная !!!)

Enum были впервые представлены в Java1.5 ( Sep'2004 ), и bug требующая разрешения на включение String, была подана долго назад ( Oct'95 ). Если вы посмотрите комментарий, опубликованный на этой ошибке в июне 2004 года , в нем говорится: « Don't hold your breath. Nothing resembling this is in our plans. Don't hold your breath. Nothing resembling this is in our plans. Похоже, что они отложили ( проигнорировали ) эту ошибку и в итоге запустили Java 1.5 в том же году, когда они представили «enum» с порядковым номером начиная с 0 и решили ( пропустили ) не поддерживать нуль для перечисления. Позже в Java1.7 ( Jul'2011 ) они следовали ( принудительно ) с той же философией со String (т. Е. При генерации байт-кода не выполнялась нулевая проверка перед вызовом метода hashcode ()).

Поэтому я думаю, что это сводится к тому, что перечисление было первым и было реализовано с его порядковым номером начиная с 0, из-за чего они не могли поддерживать нулевое значение в блоке switch, а затем со String они решили принудительно использовать ту же философию, то есть нулевое значение не разрешено в блоке переключателя.

TL; DR С помощью String они могли бы позаботиться о NPE (вызванном попыткой генерировать hashcode для null) при реализации кода Java для преобразования байтового кода, но, наконец, решили не делать этого.

Ссылка: bug , JavaVersionHistory , JavaCodeToByteCode , SO


Answer #2

В общем случае null - это противно обращаться; возможно, лучший язык может жить без null .

Ваша проблема может быть решена

    switch(month==null?"":month)
    {
        ...
        //case "":
        default: 
            monthNumber = 0;

    }

Answer #3

Как отмечает damryfbfnetsi в комментариях, JLS §14.11 имеет следующее примечание:

Запрет на использование null в качестве метки коммутатора не позволяет писать код, который никогда не может быть выполнен. Если выражение switch имеет ссылочный тип, то есть String или примитивный тип в виде бокса или тип перечисления, тогда возникает ошибка времени выполнения, если выражение во время выполнения принимает значение null . По мнению разработчиков языка программирования Java, это лучший результат, чем молча пропустить весь оператор switch или выбрать выполнение операторов (если они есть) после метки по default (если есть).

(акцент мой)

В то время как последнее предложение пропускает возможность использования case null: это кажется разумным и предлагает взгляд на намерения дизайнеров языка.

Если мы скорее посмотрим на детали реализации, то в этом блоге Христианом Хужером есть некоторые проницательные предположения о том, почему null не разрешен в коммутаторах (хотя он сосредоточен на переключателе enum а не на переключателе String ):

Под капотом оператор switch обычно компилируется в байтовый код tablesswitch. И «физический» аргумент для switch а также его случаи - это int s. Значение int для включения определяется путем вызова метода Enum.ordinal() . [...] ординалы начинаются с нуля.

Это означает, что отображение null в 0 не будет хорошей идеей. Коммутатор первого значения перечисления будет неотличим от нуля. Может быть, было бы неплохо начать считать ординалы для перечислений в 1. Однако это не было определено так, и это определение не может быть изменено.

В то время как String переключатели реализованы по-разному , переключатель enum появился первым и установил прецедент того, как следует переключать тип ссылки, когда ссылка равна null .


Answer #4

Согласно Java Docs:

Коммутатор работает с байтовыми, короткими, char и int примитивными типами данных. Он также работает с перечисляемыми типами (обсуждаются в типах Enum), классом String и несколькими специальными классами, которые обертывают некоторые примитивные типы: символ, байт, короткий и целочисленный (обсуждаются в Numbers and Strings).

Поскольку null не имеет типа и не является экземпляром чего-либо, он не будет работать с оператором switch.


Answer #5

Это попытка ответить, почему он выбрасывает NullPointerException

Вывод команды javap ниже показывает, что case выбирается на основе хэш- .hashCode() строки аргумента switch и, следовательно, выдает NPE, когда .hashCode() вызывается в нулевой строке.

6: invokevirtual #18                 // Method java/lang/String.hashCode:()I
9: lookupswitch  { // 3
    -1826660246: 44
     -263893086: 56
      103666243: 68
        default: 95
   }

Это означает, что на основе ответов на hashCode Can 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

так как вы можете видеть, что только один случай генерируется, но с двумя условием, чтобы проверить mach с каждой строкой case. Очень интересный и сложный способ реализации этой функциональности!


Answer #6

Это не очень, но 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;




language-design