Java 12 - wyrażenie switch (preview feature)
Poniższy post stanowi luźny przegląd dokumentaji JEP-361 Switch expressions, wprowadzonych ostatecznie w javie 14.
Nowości w języku
Status “preview feature”
Java 12 wprowadza wyrażenie switch jako ulepszoną alternatywę dla istniejącej od zarania dziejów instrukcji switch. Jest to nowość dostępna jako preview feature, co oznacza, że jej użycie w kodzie kompilowanym jako --source 12
wymaga podania opcji --enable-preview
zarówno do kompilatora jak i launchera.
Na przykład, aby skompilować Simple.java pod jdk 12, należy użyć polecenia:
|
|
Kompilacja kodu wykorzystującego elementy języka o statusie prevew
skutkuje wypisaniem następującego komunikatu:
|
|
Instrukcja switch
Istniejący w javie switch
jest instrukcją, nie wyrażeniem. Jest to instrukcja niskopoziomowa, zaimplementowana dla zachowania “zgodności mentalnej” programistów używających wcześniej C lub C++. Używana również w wysokopoziomowych zastosowaniach w Javie od dawna sprawia kilka problemów. Są to:
- Nieintuicyjne i podatne na błędy zachowanie fallthrough, czyli wykonanie kodu wszystkich następujących po bieżącym
case
przypadków, jeśli bieżącycase
nie kończy się instrukcjąbreak
. Większość przypadków użyciacase
nie korzysta z właściwości fallthrough, co wymusza pisaniebreak
w każdymcase
.
|
|
- Wiele instrukcji
break
w mocno rozgałęzionymcase
zmniejsza czytelność kodu. - Zakresem leksykalnym zmiennej zadeklarowanej w pewnej gałęzi
case
jest cały kod od jej deklaracji do końcaswitch
, co sprawia, że zmienne tymczasowe - reprezentujące ten sam koncept w każdej z gałęzi - muszą się w każdej gałęzi inaczej nazywać. Bardziej odpowiednie byłoby zawężenie zakresu do bieżącej gałęzi.
|
|
- Istniejące użycia
switch
często symulują działanie wyrażenia przez przypisywanie w kolejnych gałęziach wartości do tej samej, zadeklarowanej przed instrukcjąswitch
, zmiennej. Znów - zmniejsza to czytelność kodu i wydaje się nadmiarowe.
|
|
Wyrażenie switch (preview)
Wszystkie powyższe problemy rozwiązuje wyrażenie switch
.
- wprowadzona jest nowa forma labela dla
switch
, tzw. arrow label, w postacicase L ->
, przy czym możliwe jest podanie wielu labelek oddzielonych przecinkiem w jednej gałęzicase
. Powyższy przykład z dniami tygodnia będzie miał dużo czytelniejszą postać:
|
|
- domyślnie nie występuje fallthrough, wykonywany jest jedynie kod po strzałce (
->
) switch
jako wyrażenie zwraca wartość, którą można przypisać do zmiennej. Można więc napisać znacznie bardziej spójny kod:
|
|
- jeśli programista pragnie użyć semantyki
falthrough
w wyrażeniuswitch
, może to zrobić, używając tradycyjnej formy labela, czyli tzw. colon label, w postacicase L:
Instrukcja yield
W przypadku, gdy w danej gałęzi wykonywanych jest wiele instrukcji, należy je ogarnąć wąsatymi nawiasami ({ ... }
), a wartość należy zwrócić za pomocą instrukcji yield
w postaci yield value;
. Instrukcja ta może być używana wyłącznie w wyrażeniu switch
ale nie w instrukcji switch
, w której z gałęzi można “wyjść” albo przez fallthrough albo przy użyciu instrukcji break
.
|
|
Wyczerpowalność (exhaustiveness)
Jeśli istnieje polskie tłumaczenie słowa exhaustiveness, to ja go chyba nie znam. O co chodzi z wyczerpywalnością? Otóż kompilator gwarantuje, że dla dowolnej wartości w wyrażeniu switch
zawsze istnieje gałąż case
. (Warto przypomnieć, że instrukcja switch takich gwarancji nie daje). Jak to jest możliwe? Oczywiście, gwarancja polega na tym, że kompilator rzuci wyjątkiem, gdy wszystkie przypadki nie są pokryte.
W instrukcji switch
, jeśli wybór gałęzi dotyczy typu Enum
, kompilator gwarantuje, że pokryta jest każda stała (i że definicja klasy Enum
nie zmieniła się między kompilacją a uruchomieniem) w taki sposób, że kompilator generuje automatycznie przypadek default
. Jeśli kod zostanie przekompilowany, a ktoś w międzyczasie rozszerzył definicję klasy Enum
, kompilator wykryje brak pokrycia (a więc fakt, że nie wszyskie przypadki są wyczerpane czy użyte) i elegancko wyrzuci błąd. Oczywiście, możemy “zadowolić” kompilator pisząc ręcznie gałąź default
, i wówczas już zachowanie kodu w przypadku “niepokrytych” wartości zależy od programisty.
Wymóg zwrócenia wartości
Wyrażenie switch
musi zwracać w każdej gałęzi wartość (kończyć się normalnie z pewną wartością) lub rzucić wyjątek (kończyć się gwałtownie z wyjątkiem).
|
|
Oznacza to, że nie można “uciec” z instrukcji switch
w żaden inny sposób, na przykład przez użycie instrukcji continue
:
|
|
Ten wpis jest częścią serii java.
- 2021-09-12 - Java 18: co nowego? - przegląd JEP-ów
- 2021-21-09 - Java 17 - RandomGenerator i spółka
- 2021-15-09 - Java 17 - co nowego?
- 2021-04-03 - Java 15 - czym są sealed classes?
- 2021-26-02 - Java 13 i 14: Bloki tekstowe i rekordy
- 2021-24-02 - Java 12 - wyrażenie switch (preview feature)
- 2021-23-02 - Java 11 - HTTP Client i uruchamianie jednoplikowych programów
- 2021-18-02 - Java 9 - co to jest JShell i dlaczego warto używać REPL-a w Javie
- 2021-12-02 - Java 10 - var, nowe metody w Optional, kolekcje "unmodifiable"
- 2021-11-02 - Java 9 - nowości w bibliotece
- 2021-10-02 - Java 9: czy mogę stworzyć z mojej aplikacji binarkę?
- 2021-10-02 - Java 9: praktyczny przykład - trzy moduły
- 2021-08-02 - Java 8: praktyczny przykład - przewidywanie kolejnej daty w serii