Как использовать нуль в switch


202
Integer i = ...

switch (i){
    case null:
        doSomething0();
        break;    
    }

В приведенном выше коде я не могу использовать null в операторе switch case. Как я могу сделать это по-другому? Я не могу использовать, defaultпотому что тогда я хочу сделать что-то еще.


9
перед переключением проверьте состояние нуля, если (i == null) {// dosomething}
— Нагараджу Бадаени

8
Это фактически сделает переключение полезным. Другие языки сопоставления с образцом работают таким образом.
— Пиролистический

Ответы:


277

Это невозможно с switchзаявлением на Java. Проверьте nullдо switch:

if (i == null) {
    doSomething0();
} else {
    switch (i) {
    case 1:
        // ...
        break;
    }
}

Вы не можете использовать произвольные объекты в switchвыражениях * . Причина , по которой компилятор не жалуется , switch (i)где iэто Integerпроисходит потому , что Java автоматически unboxes IntegerАнь int. Как уже сказал Ассилиас, распаковка будет, NullPointerExceptionкогда iесть null.

* Начиная с Java 7 вы можете использовать Stringв switchзаявлениях.

Подробнее о switch(включая пример с нулевой переменной) в Oracle Docs - Switch


16
Вы также можете использовать перечисления в выражениях switch.
— Йорики

27
Имеет смысл, что вы не можете использовать нулевое целое число или другой класс Wrapper из-за распаковки. Но как насчет перечислений и строк? Почему они не могут быть нулевыми?
— Луан Нико

9
Я не понимаю, почему короткое замыкание нулевого значения, отображаемое в случай «по умолчанию» или специальный случай для нулевого переключателя, не было реализовано для строк. Это делает использование ключей для упрощения кода бессмысленным, так как вы всегда должны делать нулевую проверку. Я не говорю, что упрощение - единственное использование для выключателей все же.
— Реймиус

3
@ Реймиус, ты не всегда должен делать нулевую проверку. Если вы уважаете контракты кода , которые вы даете свои методы, почти всегда удается не иметь ваш код завалена проверки нуля. Использование утверждений всегда приятно.
— Джоффри

Я также хотел бы знать ответ на запрос @ LuanNico. Это кажется неразумным , что nullне может быть действительным случаем при работе с Stringи enumтипов. Возможно, enumреализация основывается на вызове ordinal()за кулисами (хотя, даже если это так, почему бы не считать, nullчто он имеет «порядковый номер» -1?), А Stringверсия делает что-то с использованием intern()сравнения и сравнения указателей (или иным образом полагается на то, что строго требует разыменования объект)?
— aroth

98
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}

более чистый способ, чем использование одного дополнительного, если еще
— Vivek Agrawal

40

switch(i)выдаст исключение NullPointerException, если оно есть null, потому что оно попытается распаковать Integerв int. Так что case null, что оказывается незаконным, никогда бы не было достигнуто в любом случае.

Вы должны проверить, что я не нуль перед switchутверждением.


23

Документы Java четко заявили, что:

Запрет на использование null в качестве метки переключателя не позволяет писать код, который никогда не будет выполнен. Если выражение-переключатель относится к ссылочному типу, такому как тип примитива в штучной упаковке или перечисление, произойдет ошибка времени выполнения, если выражение оценивается как ноль во время выполнения.

Перед выполнением оператора Swithch вы должны проверить наличие нуля.

if (i == null)

См . Заявление о переключении

case null: // will never be executed, therefore disallowed.

1
В javadocs по вашей ссылке больше не написано «Запрет на использование null в качестве метки переключателя [и т. Д.]».
— Патрик М


14

Дано:

public enum PersonType {
    COOL_GUY(1),
    JERK(2);

    private final int typeId;
    private PersonType(int typeId) {
        this.typeId = typeId;
    }

    public final int getTypeId() {
        return typeId;
    }

    public static PersonType findByTypeId(int typeId) {
        for (PersonType type : values()) {
            if (type.typeId == typeId) {
                return type;
            }
        }
        return null;
    }
}

Для меня это обычно совпадает с таблицей поиска в базе данных (только для редко обновляемых таблиц).

Однако, когда я пытаюсь использовать findByTypeIdв операторе switch (скорее всего, из пользовательского ввода) ...

int userInput = 3;
PersonType personType = PersonType.findByTypeId(userInput);
switch(personType) {
case COOL_GUY:
    // Do things only a cool guy would do.
    break;
case JERK:
    // Push back. Don't enable him.
    break;
default:
    // I don't know or care what to do with this mess.
}

... как уже говорили другие, это приводит к NPE @ switch(personType) {. Одним из обходных путей (т. Е. «Решением»), который я начал реализовывать, было добавление UNKNOWN(-1)типа.

public enum PersonType {
    UNKNOWN(-1),
    COOL_GUY(1),
    JERK(2);
    ...
    public static PersonType findByTypeId(int id) {
        ...
        return UNKNOWN;
    }
}

Теперь вам не нужно выполнять нулевую проверку, когда она имеет значение, и вы можете выбирать, обрабатывать или не обрабатывать UNKNOWNтипы. (ПРИМЕЧАНИЕ: -1маловероятный идентификатор в бизнес-сценарии, но, очевидно, выберите то, что имеет смысл для вашего варианта использования).


2
UNKNOWNлучшее решение по этому вопросу, которое я когда-либо видел, и завышенные нуль-проверки.
— Membersoundound

5

Вы должны сделать

if (i == null) {
   doSomething0();
} else {
   switch (i) {
   }
}

4

Некоторые библиотеки пытаются предложить альтернативы встроенному switchвыражению Java . Vavr является одним из них, они обобщают это для сопоставления с образцом.

Вот пример из их документации :

String s = Match(i).of(
    Case($(1), "one"),
    Case($(2), "two"),
    Case($(), "?")
);

Вы можете использовать любой предикат, но они предлагают многие из них из коробки, и $(null)это совершенно законно. Я считаю, что это более элегантное решение, чем альтернативы, но для этого требуется java8 и зависимость от библиотеки vavr ...




0

Ты не можешь Вы можете использовать примитивы (int, char, short, byte) и String (только строки в Java 7) в переключателе. примитивы не могут быть нулевыми.
Проверьте iв отдельном состоянии перед выключателем.


4
Вы также можете использовать перечисления.
— Кару

5
если перечисление равно нулю, у вас будет та же проблема. Кстати, довольно странно, что коммутатор не может обрабатывать нуль, поскольку у него есть предложение по умолчанию

1
@LeonardoKenji Предложение по умолчанию не имеет ничего общего с нулем; что бы вы ни включали, оно будет разыменовано для проверки любых других случаев, поэтому предложение по умолчанию не будет обрабатывать нулевой случай (исключение NullPointerException выдается до того, как у него появится шанс).
— Бен

2
Я думаю, он имел в виду, что предложение по умолчанию должно обрабатывать нуль, как любое другое возможное значение перечисления, которое не было поймано предыдущим случаем
— Лев

0

Просто подумайте, как может работать ПЕРЕКЛЮЧАТЕЛЬ,

  • в случае примитивов мы знаем, что он может потерпеть неудачу с NPE для автобокса
  • но для String или enum это может быть вызванный метод equals, который, очевидно, нуждается в значении LHS, для которого вызывается equals. Таким образом, если никакой метод не может быть вызван с нулевым значением, переключатель не может обработать нулевое значение.

0

На основе ответа @tetsuo, с Java 8:

Integer i = ...

switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
    case DEFAULT_VALUE:
        doDefault();
        break;    
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.