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

202
Integer i = ...

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

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

Hudi
источник
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

Jesper
источник
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утверждением.

assylias
источник
23

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

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

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

if (i == null)

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

case null: // will never be executed, therefore disallowed.
Shehzad
источник
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 ...

Эммануэль Тузери
источник
2
switch (String.valueOf(value)){
    case "null":
    default: 
}
l0v3
источник
0

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

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

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

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

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

Integer i = ...

switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
    case DEFAULT_VALUE:
        doDefault();
        break;    
}
Calfater
источник