Cómo usar nulo en el interruptor

202
Integer i = ...

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

En el código anterior, no puedo usar nulo en la declaración de cambio de caso. ¿Cómo puedo hacer esto de manera diferente? No puedo usar defaultporque quiero hacer otra cosa.

hudi
fuente
9
antes del cambio, verifique la condición nula si (i == nulo) {// dosomething}
Nagaraju Badaeni
8
Esto realmente haría que el cambio sea útil. Otros lenguajes de coincidencia de patrones funcionan de esta manera.
Pirolístico

Respuestas:

277

Esto no es posible con una switchdeclaración en Java. Consulte nullantes de switch:

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

No puede usar objetos arbitrarios en las switchdeclaraciones * . La razón por la que el compilador no se queja de switch (i)dónde ies un Integeres porque Java desempaqueta automáticamente Integera un int. Como ya dijo asilias, el unboxing arrojará un NullPointerExceptioncuándo ies null.

* Desde Java 7 puede usar Stringen switchdeclaraciones.

Más información switch(incluido el ejemplo con variable nula) en Oracle Docs - Switch

Jesper
fuente
16
También puede usar enumeraciones en las declaraciones de cambio.
joriki
27
Tiene sentido que no pueda usar un entero nulo u otra clase de envoltura, debido al desempaquetado. ¿Pero qué pasa con las enumeraciones y las cadenas? ¿Por qué no pueden ser nulos?
Luan Nico
9
No entiendo por qué no se implementó un cortocircuito de nulo en el caso "predeterminado" o un caso especial para un interruptor nulo para Strings. Hace que el uso de interruptores para simplificar el código no tenga sentido ya que siempre debe hacer una verificación nula. Sin embargo, no digo que la simplificación sea el único uso para los interruptores.
Reimius
3
@Reimius, no siempre tienes que hacer una comprobación nula. Si respeta los contratos de código que le da a sus métodos, casi siempre puede lograr que su código no esté lleno de verificaciones nulas. Sin embargo, usar afirmaciones siempre es bueno.
Joffrey
También me gustaría saber la respuesta a la consulta de @ LuanNico. Parece irrazonable que nullno pueda ser un caso válido cuando se trabaja con Stringy enumtipos. Quizás la enumimplementación se basa en llamar ordinal()detrás de escena (aunque aún así, ¿por qué no tratar nullcomo si tuviera un 'ordinal' de -1?), Y la Stringversión hace algo usando intern()una comparación de puntero (o de lo contrario se basa en algo que estrictamente requiere desreferenciar un objeto)?
Aroth
98
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}
tetsuo
fuente
de manera más limpia que usar un extra si más
Vivek Agrawal
40

switch(i)lanzará una NullPointerException si lo es null, porque intentará desempaquetar Integeren un int. Entoncescase null , lo que resulta ser ilegal, nunca habría sido alcanzado de todos modos.

Debe verificar que no sea nulo antes de la switchdeclaración.

asilias
fuente
23

Los documentos de Java declararon claramente que:

La prohibición de usar nulo como etiqueta de interruptor evita que uno escriba código que nunca se puede ejecutar. Si la expresión de cambio 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 nula en tiempo de ejecución.

Debe tener que verificar nulo antes de la ejecución de la declaración Swithch.

if (i == null)

Ver la declaración de cambio

case null: // will never be executed, therefore disallowed.
Shehzad
fuente
1
Los javadocs en su enlace ya no dicen "La prohibición de usar nulo como etiqueta de interruptor [etc.]".
Patrick M
14

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 generalmente se alinea con una tabla de búsqueda en una base de datos (solo para tablas que rara vez se actualizan).

Sin embargo, cuando trato de usar findByTypeIden una declaración de cambio (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.
}

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

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

Ahora, no tiene que hacer una verificación nula donde cuenta y puede elegir manejar o no los UNKNOWNtipos. (NOTA: -1es un identificador poco probable en un escenario empresarial, pero obviamente elige algo que tenga sentido para su caso de uso).

Beez
fuente
2
UNKNOWNes la mejor solución que he visto en mi vida y supero los nullchecks.
membersound
5

Tienes que hacer un

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

Algunas bibliotecas intentan ofrecer alternativas a la switchdeclaración integrada de Java . 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($(), "?")
);

Puede usar cualquier predicado, pero ofrecen muchos de ellos listos para usar, 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 ...

Emmanuel Touzery
fuente
2
switch (String.valueOf(value)){
    case "null":
    default: 
}
l0v3
fuente
0

No puedes Puede usar primitivas (int, char, short, byte) y String (Strings en java 7 solamente) en el switch. Las primitivas no pueden ser nulas.
Compruebe ien condiciones separadas antes del interruptor.

Mikita Belahlazau
fuente
44
También puedes usar enumeraciones.
Karu
55
Si la enumeración es nula, tendrá el mismo problema. Por cierto, es bastante extraño que el interruptor no pueda manejar nulo, ya que tiene una cláusula predeterminada
1
@LeonardoKenji La cláusula predeterminada realmente no tiene nada que ver con nulo; lo que sea que esté activando, se desreferenciará para verificar cualquier otro caso, por lo que la cláusula predeterminada no manejará el caso nulo (se lanza una NullPointerException antes de que tenga la oportunidad).
Ben
2
Creo que se refería a que la cláusula predeterminada debería manejar nulo como cualquier otro valor de enumeración posible que no fue detectado por el caso anterior
Leo
0

Solo considere cómo podría funcionar el INTERRUPTOR,

  • en caso de primitivas, sabemos que puede fallar con NPE para el auto-boxeo
  • pero para String o enum , podría estar invocando el método equals, que obviamente necesita un valor LHS en el que se invoque equals. Entonces, dado que no se puede invocar ningún método en un valor nulo, el interruptor no puede manejar nulo.
Puneet
fuente
0

Basado en la respuesta @tetsuo, con java 8:

Integer i = ...

switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
    case DEFAULT_VALUE:
        doDefault();
        break;    
}
Calfater
fuente