Cómo usar null en switch


Integer i = ...

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

En el código anterior no puedo usar null en la instrucción switch case. ¿Cómo puedo hacer esto de manera diferente? No puedo usar default porque entonces quiero hacer otra cosa.

Author: Vic, 2012-04-26

11 answers

Esto no es posible con una instrucción switch en Java. Compruebe null antes del switch:

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

No se pueden usar objetos arbitrarios en las sentencias switch *. La razón por la que el compilador no se queja de switch (i) donde i es un Integer es porque Java auto-desboxea el Integer a un int. Como asilias ya dijo, el unboxing lanzará un NullPointerException cuando i es null.

* Desde Java 7 puedes usar String en las sentencias switch.

Más sobre switch (incluyendo ejemplo con variable nula) en Oracle Docs-Switch

 206
Author: Jesper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-12 19:07:46
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}
 58
Author: tetsuo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-02 16:04:20

switch(i) lanzará una excepción NullPointerException si i es null, porque intentará descomprimir el Integer en un int. Así que case null, que resulta ser ilegal, nunca habría sido alcanzado de todos modos.

Debe comprobar que i no es null antes de la instrucción switch.

 31
Author: assylias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-27 08:12:17

Java docs declaró claramente que:

La prohibición de usar null como etiqueta de switch impide escribir código que nunca se puede ejecutar. Si la expresión switch es de un tipo de referencia, como un tipo primitivo en caja o una enumeración, se producirá un error en tiempo de ejecución si la expresión se evalúa como null en tiempo de ejecución.

Debe verificar null antes de la ejecución de la sentencia Swithch.

if (i == null)

Ver La Instrucción Switch

case null: // will never be executed, therefore disallowed.
 17
Author: Shehzad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-26 11:14:48

Dado:

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;
    }
}

Para mí, esto normalmente se alinea con una tabla de búsqueda en una base de datos (solo para tablas raramente actualizadas).

Sin embargo, cuando intento usar findByTypeId en una instrucción switch (de, muy probablemente, la entrada del usuario)...

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.
}

As como otros han dicho, esto resulta en un NPE @ switch(personType) {. Una solución (es decir, "solución") que comencé a implementar fue agregar un tipo UNKNOWN(-1).

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

Ahora, usted no tiene que hacer null-checking donde cuenta y usted puede elegir, o no para, manejar los tipos UNKNOWN. (NOTA: -1 es un identificador poco probable en un escenario de negocio, pero obviamente elige algo que tenga sentido para tu caso de uso).

 10
Author: Beez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-02 21:48:45

Tienes que hacer un

if (i == null) {
   doSomething0();
} else {
   switch (i) {
   }
}
 5
Author: Kai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-26 11:07:02

Un switch funciona con los datos primitivos byte, short, char e int tipo. También funciona con tipos enumerados (discutidos en Tipos de enumeración), la clase String, y algunas clases especiales que envuelven ciertos tipos primitivos: Carácter, Byte, Corto, y Entero (discutido en Números y Cadenas).

Dado que null no tiene tipo, y no es una instancia de nada, lo hará no funciona con una sentencia switch.

  1. Tenary * interruptor interior:

    // how we could read it:
    // if 'T' reference is not 'null' use 'T' else 'DEFAULT'
    // 
    switch(T != null ? T : DEFAULT) {              
       case DEFAULT: 
       break;
    }
    
  2. Ejemplo activar la referencia de cadena:

    switch(String == null ? "null" : String) {
        case "null": 
        break;
    }
    
  3. Ejemplo encendido (Cadena) usando Objeto(referencia).toString () método***:

    // or use implementing String class function 
    // String.valueOf(Object)
    switch(Object != null ? Object.toString() : "null") {
        case "null":
        break;
    }
    

    * * * no se recomienda - ¿qué pasa cuando Objeto.toString () devuelve null String reference???

  4. Otra solución sin comprobación nula:

Sí!!! Me encanta usar TRY / CATCH o LANZAR una nueva excepción como si / ELSE reemplazo :)

    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
        }
    }

Editar - en respuesta:

Para su última solución: No programe Python en Java. - glglgl Feb 15 at 14:33

...Quería decir: En Python, las excepciones se consideran como un método normal de control de flujo (EAFP). En Java, se consideran como lo que su nombre dice: excepciones. Son bastante caros y deben usarse con cuidado. A diferencia de Python, use TRY / CATCH o THROW new Exception como SI el reemplazo de / ELSE no fuera bueno aquí. - glglgl

Programación con Excepciones

Se pueden usar excepciones para ayudar a escribir robustas ^1 programas. Proporcionan un enfoque organizado y estructurado de la robustez. Sin excepciones, un programa puede saturarse con sentencias if que prueban varias condiciones de error posibles. Con excepciones, se hace posible escribir una implementación limpia de un algoritmo que manejará todos los casos normales. Los casos excepcionales se pueden manejar en otro lugar, en una cláusula de captura de un intento instrucción.

Cuando un programa encuentra una condición excepcional y no tiene forma de manejarla inmediatamente, el programa puede lanzar una excepción. En algunos casos, tiene sentido lanzar una excepción perteneciente a una de las clases predefinidas de Java, como IllegalArgumentException o IOException. Sin embargo, si no hay una clase estándar que represente adecuadamente la condición excepcional, el programador puede definir una nueva clase de excepción. La nueva clase debe extender la clase estándar Arrojar o una de sus subclases. En general, si el programador no desea requerir el manejo obligatorio de excepciones, la nueva clase extenderá RuntimeException (o una de sus subclases). Para crear una nueva clase de excepción comprobada, que requiere un manejo obligatorio, el programador puede extender una de las otras subclases de Excepción o puede extender la Excepción misma.

Aquí, por ejemplo, hay una clase que extiende la Excepción, y por lo tanto requiere el manejo obligatorio de excepciones cuando usado:

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

^1 un lugar donde la corrección y la robustez son importantes, y especialmente difíciles, es en el procesamiento de los datos de entrada, ya sea que los datos sean ingresados por el usuario, leídos de un archivo o recibidos a través de una red.

Aún mejor aquí puedo darte un ejemplo para la aplicación de 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);
    }
}
 3
Author: ceph3us,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-15 17:40:17

Algunas bibliotecas intentan ofrecer alternativas a la instrucción incorporada java switch. Vavr es uno de ellos, lo generalizan a la coincidencia de patrones.

Aquí hay un ejemplo de su documentación :

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

Puedes usar cualquier predicado, pero ofrecen muchos de ellos fuera de la caja, y $(null) es perfectamente legal. Me parece una solución más elegante que las alternativas, pero esto requiere java8 y una dependencia de la biblioteca vavr...

 2
Author: Emmanuel Touzery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-25 12:10:46

No puede. Puede usar primitivas (int, char, short, byte) y String (solo Cadenas en java 7) en switch. los primitivos no pueden ser nulos.
Compruebe i en condiciones separadas antes del cambio.

 0
Author: Nikita Beloglazov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-26 11:06:03

Switch (Cadena.valueOf(valor)){ caso "null": predeterminado: }

 0
Author: l0v3,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-30 10:58:12

También puedes usar String.valueOf((Object) nullableString) como

switch (String.valueOf((Object) nullableString)) {
case "someCase"
    //...
    break;
...
case "null": // or default:
    //...
        break;
}

Ver interesante TAN Q / A: ¿Por qué String?valueOf (null) lanza una excepción NullPointerException

 0
Author: oikonomopo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-06 12:33:16