Jak używać null w przełączniku

202
Integer i = ...

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

W powyższym kodzie nie mogę użyć null w instrukcji case switch. Jak mogę to zrobić inaczej? Nie mogę korzystać default, bo wtedy chcę zrobić coś innego.

Hudi
źródło
9
przed przełączeniem sprawdź stan zerowy, jeśli (i == null) {// dosomething}
Nagaraju Badaeni 26.04.
8
To faktycznie uczyniłoby przełącznik przydatnym. Inne języki dopasowania wzorców działają w ten sposób.
Pirolistyczny

Odpowiedzi:

276

Nie jest to możliwe w przypadku switchinstrukcji w Javie. Sprawdź nullprzed switch:

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

Nie można używać dowolnych obiektów w switchinstrukcjach * . Powodem, dla którego kompilator nie narzeka na to, switch (i)gdzie ijest, jest Integerto, że Java automatycznie rozpakowuje Integerplik na int. Jak już powiedzieli assyli, rozpakowanie rzuci NullPointerExceptionkiedy ibędzie null.

* Od wersji Java 7 można używać Stringw switchinstrukcjach.

Więcej informacji switch(w tym przykład ze zmienną null) w dokumentacji Oracle Docs - Switch

Jesper
źródło
16
Możesz także użyć wyliczeń w instrukcjach przełączników.
joriki
27
Ma to sens, że nie można użyć zerowej liczby całkowitej lub innej klasy opakowania z powodu rozpakowania. A co z wyliczeniami i łańcuchami? Dlaczego nie mogą być zerowe?
Luan Nico,
9
Nie rozumiem, dlaczego zwarcie wartości zerowej jest mapowane na przypadek „domyślny” lub przypadek specjalny przełącznika zerowego nie został zaimplementowany dla ciągów. Ułatwia to stosowanie przełączników w celu uproszczenia kodu, ponieważ zawsze trzeba wykonać kontrolę zerową. Nie twierdzę jednak, że uproszczenie jest jedynym zastosowaniem przełączników.
Reimius
3
@ Reimius nie zawsze musisz wykonać kontrolę zerową. Jeśli przestrzegasz kontraktów kodowych, które podajesz swoim metodom, prawie zawsze możesz nie dopuścić do zaśmiecenia kodu zerowymi kontrolami. Używanie asertów jest zawsze przyjemne.
Joffrey,
Chciałbym również poznać odpowiedź na zapytanie @ LuanNico. Wydaje się nieuzasadnione, że nullnie może to być poprawny przypadek podczas pracy z Stringi enumtypami. Być może enumimplementacja polega na wywołaniu ordinal()za kulisami (chociaż mimo to, dlaczego nie traktować jej nulljako „porządkowej” -1?), A Stringwersja robi coś przy użyciu intern()i porównuje wskaźniki (lub w inny sposób opiera się na czymś, co ściśle wymaga dereferencji obiekt)?
aroth
98
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}
tetsuo
źródło
czystszy sposób niż korzystanie z jednego dodatkowego, jeśli inaczej
Vivek Agrawal
40

switch(i)wyrzuci NullPointerException, jeśli tak null, ponieważ spróbuje rozpakować plik Integerw int. Tak więc case null, co okazuje się nielegalne, i tak nigdy nie zostałoby osiągnięte.

Musisz sprawdzić, czy i nie jest zerowy przed switchinstrukcją.

assylias
źródło
23

Dokumenty Java wyraźnie stwierdzają, że:

Zakaz używania wartości null jako etykiety przełącznika uniemożliwia pisanie kodu, którego nigdy nie można wykonać. Jeśli wyrażenie przełączające jest typu referencyjnego, takiego jak pierwotny element w pudełku lub wyliczenie, wystąpi błąd w czasie wykonywania, jeśli wyrażenie będzie miało wartość zerową w czasie wykonywania.

Przed wykonaniem instrukcji Swithch musisz sprawdzić, czy wartość jest pusta.

if (i == null)

Zobacz instrukcję Switch

case null: // will never be executed, therefore disallowed.
Shehzad
źródło
1
Jawadoki w twoim linku nie mówią już „Zakaz używania wartości null jako etykiety przełącznika [itd.]”.
Patrick M
14

Dany:

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

Dla mnie zwykle jest to zgodne z tabelą przeglądową w bazie danych (tylko dla tabel rzadko aktualizowanych).

Jednak gdy próbuję użyć findByTypeIdw instrukcji switch (najprawdopodobniej z danych wejściowych użytkownika) ...

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

... jak stwierdzili inni, skutkuje to NPE @ switch(personType) {. Jednym obejściem (tj. „Rozwiązaniem”), które zacząłem wdrażać, było dodanie UNKNOWN(-1)typu.

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

Teraz nie musisz sprawdzać wartości null tam, gdzie się liczy, i możesz wybrać obsługę UNKNOWNtypów. (UWAGA: -1jest mało prawdopodobnym identyfikatorem w scenariuszu biznesowym, ale oczywiście wybierz coś, co ma sens w twoim przypadku użycia).

Beez
źródło
2
UNKNOWNto najlepsze rozwiązanie w tym zakresie, jakie kiedykolwiek widziałem i które pokonało nullchecks.
Membersound
5

Musisz zrobić

if (i == null) {
   doSomething0();
} else {
   switch (i) {
   }
}
Kai
źródło
4

Niektóre biblioteki próbują oferować alternatywy dla wbudowanej switchinstrukcji Java . Vavr jest jednym z nich, uogólniają go do dopasowywania wzorców.

Oto przykład z ich dokumentacji :

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

Możesz użyć dowolnego predykatu, ale oferuje on wiele z nich po wyjęciu z pudełka i $(null)jest całkowicie legalny. Uważam to za bardziej eleganckie rozwiązanie niż alternatywy, ale wymaga to java8 i zależności od biblioteki vavr ...

Emmanuel Touzery
źródło
2
switch (String.valueOf(value)){
    case "null":
    default: 
}
l0v3
źródło
0

Nie możesz Możesz używać prymitywów (int, char, short, byte) i String (Strings in java 7 only) w przełączniku. prymitywy nie mogą mieć wartości zerowej.
Sprawdź iw osobnym stanie przed zmianą.

Mikita Belahlazau
źródło
4
Możesz także użyć wyliczeń.
Karu
5
jeśli wyliczenie jest zerowe, będziesz miał ten sam problem. BTW, to dość dziwne, że przełącznik nie może obsłużyć wartości null, ponieważ ma domyślną klauzulę
1
@LeonardoKenji Domyślna klauzula tak naprawdę nie ma nic wspólnego z wartością null; cokolwiek włączysz, zostanie odesłane w celu sprawdzenia innych przypadków, więc domyślna klauzula nie będzie obsługiwać przypadku zerowego (wyjątek NullPointerEx jest zgłaszany, zanim będzie miał szansę).
Ben
2
Myślę, że miał na myśli, że domyślna klauzula powinna obsługiwać wartość null, jak każda inna możliwa wartość wyliczenia, która nie została uchwycona przez poprzednią sprawę
Leo
0

Zastanów się, jak może działać SWITCH,

  • w przypadku prymitywów wiemy, że może to zawieść z NPE do automatycznego boxowania
  • ale dla String lub wyliczenia , może być równa wywoływanie metody, która oczywiście musi wartość LHS na co równa jest wywoływany. Tak więc, biorąc pod uwagę, że żadna metoda nie może być wywołana na wartości null, przełącz uchwyt cant null.
Puneet
źródło
0

Na podstawie odpowiedzi @tetsuo z java 8:

Integer i = ...

switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
    case DEFAULT_VALUE:
        doDefault();
        break;    
}
Cielak
źródło