Como usar null no switch

202
Integer i = ...

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

No código acima, não posso usar null na instrução switch case. Como posso fazer isso de maneira diferente? Não posso usar defaultporque então quero fazer outra coisa.

hudi
fonte
9
antes do check-chave para a condição nula se (i == null) {// dosomething}
Nagaraju Badaeni
8
Isso realmente tornaria a troca útil. Outros idiomas de correspondência de padrões funcionam dessa maneira.
Pyrolistical

Respostas:

277

Isso não é possível com uma switchinstrução em Java. Verifique nullantes de switch:

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

Você não pode usar objetos arbitrários nas switchinstruções * . O motivo pelo qual o compilador não se queixa sobre switch (i)onde iestá um Integeré porque o Java descompacta automaticamente o arquivo Integerpara um int. Como assylias já disse, o unboxing vai lançar um NullPointerExceptionquando ié null.

* Desde o Java 7, você pode usar Stringem switchdeclarações.

Mais informações sobre switch(incluindo exemplo com variável nula) no Oracle Docs - Switch

Jesper
fonte
16
Você também pode usar enumerações em instruções de opção.
Joriki # 29/13
27
Faz sentido que você não possa usar um número inteiro nulo ou outra classe Wrapper, devido ao unboxing. Mas e enums e strings? Por que eles não podem ser nulos?
Luan Nico
9
Não estou entendendo por que um curto-circuito de nulo sendo mapeado para o caso "padrão" ou um caso especial para uma opção nula não foi implementado para Strings. Isso torna inútil o uso de opções para simplificar o código, pois você sempre precisa fazer uma verificação nula. Não estou dizendo que simplificação é o único uso para switches.
Reimius
3
@ Reimius, você nem sempre precisa fazer uma verificação nula. Se você respeitar os contratos de código que você concede aos seus métodos, quase sempre poderá conseguir que seu código não seja confuso com verificações nulas. Usar declarações é sempre bom, no entanto.
Joffrey #
Também gostaria de saber a resposta para a consulta de @ LuanNico. Parece irracional que nullnão possa ser um caso válido ao trabalhar com Stringe enumtipos. Talvez a enumimplementação se baseie em chamar ordinal()os bastidores (embora, ainda assim, por que não tratar nullcomo tendo um 'ordinal' de -1?), E a Stringversão faça algo usando intern()uma comparação de ponteiro (ou se baseie em algo que exija estritamente a exclusão de uma referência objeto)?
Aroth
98
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}
tetsuo
fonte
maneira mais limpa do que usar um extra, se outra coisa
Vivek Agrawal
40

switch(i)lançará uma NullPointerException se for null, porque tentará descompactar o arquivo Integerem um int. Então case null, o que é ilegal, nunca teria sido alcançado.

Você precisa verificar se i não é nulo antes da switchdeclaração.

assylias
fonte
23

Os documentos Java declararam claramente que:

A proibição de usar null como um rótulo de opção impede que alguém escreva um código que nunca pode ser executado. Se a expressão da opção for de um tipo de referência, como um tipo primitivo em caixa ou uma enumeração, ocorrerá um erro em tempo de execução se a expressão for avaliada como nula no tempo de execução.

Você deve verificar se há nulo antes da execução da instrução Swithch.

if (i == null)

Veja a declaração Switch

case null: // will never be executed, therefore disallowed.
Shehzad
fonte
1
Os javadocs no seu link não dizem mais "A proibição de usar null como um rótulo de opção [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 mim, isso normalmente está alinhado com uma tabela de pesquisa em um banco de dados (apenas para tabelas raramente atualizadas).

No entanto, quando tento usar findByTypeIdem uma instrução switch (da entrada do usuário mais provável) ...

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 outros já declararam, isso resulta em um NPE @ switch(personType) {. Uma solução alternativa (isto é, "solução") que comecei a implementar foi adicionar um UNKNOWN(-1)tipo.

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

Agora, você não precisa fazer a verificação nula onde é importante e pode optar por, ou não, manipular os UNKNOWNtipos. (NOTA: -1é um identificador improvável em um cenário de negócios, mas obviamente escolha algo que faça sentido para o seu caso de uso).

Beez
fonte
2
UNKNOWNé a melhor solução sobre isso que eu já vi e supera as verificações nulas.
membersound
5

Você tem que fazer uma

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

Algumas bibliotecas tentam oferecer alternativas à switchinstrução java embutida . O Vavr é um deles, eles o generalizam para a correspondência de padrões.

Aqui está um exemplo de sua documentação :

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

Você pode usar qualquer predicado, mas eles oferecem muitos deles imediatamente e $(null)é perfeitamente legal. Acho isso uma solução mais elegante do que as alternativas, mas isso requer java8 e uma dependência da biblioteca vavr ...

Emmanuel Touzery
fonte
2

Você também pode usar String.valueOf((Object) nullableString) como

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

Veja interessante SO Q / A: Por que String.valueOf (null) lança uma NullPointerException

oikonomopo
fonte
2
switch (String.valueOf(value)){
    case "null":
    default: 
}
l0v3
fonte
0

Você não pode. Você pode usar primitivas (int, char, short, byte) e String (Strings apenas no java 7) no switch. primitivas não podem ser nulas.
Verifique iem condições separadas antes de trocar.

Mikita Belahlazau
fonte
4
Você também pode usar enums.
Karu
5
se a enumeração for nula, você terá o mesmo problema. BTW, é muito estranho que o interruptor não pode lidar com nulo, uma vez que tem uma cláusula padrão
1
A cláusula padrão @LeonardoKenji realmente não tem nada a ver com null; o que quer que você esteja ativando será desreferenciado para verificar outros casos, para que a cláusula padrão não lide com o caso nulo (uma NullPointerException é lançada antes que seja possível).
Ben
2
Eu acho que ele quis dizer a cláusula padrão deve lidar com nulo como qualquer outro valor enum possível que não era abrangida pelo caso anterior
Leo
0

Considere como o SWITCH pode funcionar,

  • no caso de primitivos, sabemos que pode falhar com o NPE para o boxe automático
  • mas para String ou enum , pode estar invocando o método equals, que obviamente precisa de um valor LHS no qual equal está sendo invocado. Portanto, dado que nenhum método pode ser chamado em um nulo, o switch não pode manipular nulo.
Puneet
fonte
0

Com base na resposta @tetsuo, com java 8:

Integer i = ...

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