Как использовать null в переключателе

Integer i = ...

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

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

Ответ 1

Это невозможно с помощью оператора switch в Java. Проверьте на null перед switch:

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

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

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

В документах Java четко указано, что:

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

Вы должны проверить значение null перед выполнением инструкции Swithch.

if (i == null)

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

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

Ответ 5

Дано:

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 - маловероятный идентификатор в бизнес-сценарии, но, очевидно, выберите то, что имеет смысл для вашего случая использования).

Ответ 6

Вам нужно сделать

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

Ответ 7

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

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

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

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

Ответ 8

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

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

  • Простой оператор if внутри коммутатора:

    //if 'i' different from 'null' return 'i' else return 'DEFAULT'
    switch(i != null ? i : DEFAULT) {       
    
       case DEFAULT: 
    
            break;
    }
    
  • Или если вы включите String:

     String i = null;
     // equal to -> value != null ? value.toString() 
     i = String.valueOf(i); : "null";
     switch(i) {
     case "null": 
           break;
     }
    
  • Последнее решение без нулевой проверки:

Да!!! Я люблю использовать TRY/CATCH или THROW новое исключение в качестве замены IF/ELSE:)

    Integer i = null;

    try{
    switch (i) {}
    } catch(NullPointerException npx) {
       // VM implementation specific message ->
       // Caused by: java.lang.NullPointerException: 
       // Attempt to invoke virtual method 
       // 'int java.lang.Integer.intValue()' on a null object reference

        // you need to ensure child method don't throw  npx 
        // or throws some other exception in replacement of npx 
        if(npx.getMessage().indexOf("java.lang.Integer.intValue()")>=0) {
            // handle null
        }
    }

edit - в ответ:

Для вашего последнего решения: не программируйте Python в Java. - glglgl Feb 15 at 14:33

... Я хотел сказать: в Python исключения рассматриваются как обычный метод управления потоками (EAFP). В Java они рассматриваются как то, что говорит их имя: исключения. Они довольно дороги и должны использоваться с осторожностью. В отличие от Python, используйте TRY/CATCH или THROW новое исключение, поскольку замена IF/ELSE здесь не очень хороша. - glglgl

Программирование с исключениями

Исключения могут использоваться для написания надежных программ ^ 1. Они обеспечивают организованный и структурированный подход к надежности. Без исключений, программа может быть загромождена операторами if, которые проверяют различные возможные условия ошибки. С исключениями становится возможным написать чистую реализацию алгоритма, который будет обрабатывать все обычные случаи. Исключительные случаи могут быть обработаны в другом месте в предложении catch инструкции try.

Когда программа встречается с исключительным условием и не имеет возможности немедленно ее обрабатывать, программа может выдать исключение. В некоторых случаях имеет смысл генерировать исключение, принадлежащее одному из предопределенных классов Java, таких как IllegalArgumentException или IOException. Однако, если нет стандартного класса, который адекватно представляет исключительное условие, программист может определить новый класс исключений. Новый класс должен расширять стандартный класс Throwable или один из его подклассов. В общем случае, если программист не хочет требовать обязательной обработки исключений, новый класс расширяет RuntimeException (или один из его подклассов). Чтобы создать новый проверенный класс исключений, который требует обязательной обработки, программист может распространять один из других подклассов Exception или может сам распространять Исключение.

Здесь, например, это класс, который расширяет Exception и поэтому требует обязательной обработки исключений, когда он используется:

public class ParseError extends Exception {
   public ParseError(String message) {
         // Create a ParseError object containing
         // the given message as its error message.
      super(message);
   }
}

^ 1 одно место, где правильность и надежность важны и особенно трудны - это обработка входных данных, независимо от того, набирает ли это данные пользователь, читает ли из файла, или полученные по сети.

еще лучше, я могу дать вам пример приложения для Android:

/**
 * start catcher on main thread loop
 *
 * @param context - app or any other context
 * @param handler - handler to post any background thread exceptions to ui thread
 */
public static void startCatcher(Context context, Handler handler) {
    /** grab app default exception handler */
    Thread.UncaughtExceptionHandler systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
    /** the following handler is used to catch exceptions thrown in background threads */
    LogsExceptionHandler logsExceptionHandler = new LogsExceptionHandler(context, systemUncaughtHandler, handler);
    /** set to app our handler as default one */
    Thread.setDefaultUncaughtExceptionHandler(logsExceptionHandler);
    /** loop while any exception in main looper */
    while (true) try {
        /** start message que loop */
        App.log.info("Starting crash catch Looper");
        Looper.loop();
        /** if we exit unexpectedly set app default handler to initial one */
        Thread.setDefaultUncaughtExceptionHandler(systemUncaughtHandler);
        /** and throw Runtime exception */
        throw new RuntimeException("Main thread loop unexpectedly exited");
        /** catch */
    } catch (LogsExceptionHandler.BackgroundException e) {
        /** log and start exit hook same as for main ui caught exception */
        String message = "Caught the exception in the background thread " + e.getThreadName() + ", TID: " + e.getTid() + " cause: " + e.getCause();
        App.log.debug(message);
        logsExceptionHandler.startHook(e);
    } catch (Throwable e) {
        /** log and start exit hook for caught exception */
        App.log.debug("Caught the exception in the UI thread, e: {}", e);
        logsExceptionHandler.startHook(e);
    }
}

Ответ 9

switch (String.valueOf(value)){
    case "null":
    default: 
}

Ответ 11

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

Ответ 12

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

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