Java 12 – switch expression

A Java nyelv a kezdeti verziók óta támogatja a switch utasítást (switch statement), ami segít programjainkban a végrehajtás többfelé ágaztatásában. Míg az if utasítással feltételesen végrehajthatunk bizonyos parancsokat, ha egy adott feltétel teljesül – illetve az else segítségével más utasításokat ha a feltétel nem teljesül – addig a switch-csel több ágat is létrehozhatunk, hogy egy adott változó különböző értékei esetén, különböző utasításokat hajtsunk végre. A Java 12 tartalmazza a switch utasításnak egy új, kísérleti verzióját, a switch expression-t, vagyis a switch kifejezést, ahol már nem csak önálló parancsként, hanem kifejezésként is használhatjuk a switch blokkot. Először nézzük meg, hogy a Java kezdeti verziója óta elérhető switch utasítás hogy néz ki és mik a jellemzői!

Példa switch utasításra

public class TestSwitchStatement {

    public static void main(String[] args) {
        int dayOfWeek = 3;
        switch (dayOfWeek) {
        case 1:
            System.out.println("hétfő");
            break;
        case 2:
            System.out.println("kedd");
            break;
        case 3:
            System.out.println("szerda");
            break;
        case 4:
            System.out.println("csütörtök");
            break;
        case 5:
            System.out.println("péntek");
            break;
        case 6:
            System.out.println("szombat");
            break;
        case 7:
            System.out.println("vasárnap");
            break;
        }
    }

}

A fenti kód tartalmaz egy int típusú dayOfWeek nevű változót, ami azt tárolja, hogy a hét hányadik napján tartunk. Mivel a hétnek hét napja van (да!), ezért itt több if-et kéne használnunk, hogy minden esetet lefedjünk és a hét napjának sorszáma alapján kiírjuk a nap nevét a standard kimenetre. Ehelyett viszont egy switch utasítást használtunk, ami 7 ágat tartalmaz és így lefedtük a várható eseteket.

Give me a break!

Feltűnhet, hogy a fenti példában sok break utasítás is szerepel. Miért van ez?

Régebbről fakadó programozási nyelvekben, mint a C, is jelen volt a switch utasítás. Ekkor még alacsonyabb szintű nyelvi elemnek szánták ezt a parancsot, és jellemző is volt azokra a feladatokra, amikre szánták, hogy a case-ek pusztán címkék voltak, és belépési pontul szolgáltak az amúgy a switch utasítás utasításblokkjában szereplő utasításokhoz. Más szóval a case címkék csak azt jelölték, hogy hányadik sorától kell elkezdeni a végrehajtását az utasításoknak, de a végrehajtás egészen a switch utasításblokkjának végéig tartott. Ezt fall-through logic-nak hívják. A Java ezt a nyelvi elemet úgy vette át, ahogy tipikusan más nyelvekben szerepelt, ezért a Java-ban is jelen van a switch utasításnál a fall-through logic. Újonnan programozást tanulóknak sokszor összezavaró ez a megvalósítás, hisz úgy gondolnak a switch-re, mint egy kizárólagos eseteket felsorakoztató struktúrára. Pedig nem így van. Ha a fenti példában nem szerepelnének a break utasítások, akkor bár a hét napjainak sorszáma alapján a helyes case-től kezdene el futni a kód, de a futását nem hagyná abba egészen a switch végéig, vagyis kiírná, hogy szerda, csütörtök, péntek, szombat, vasárnap. Ez a funkció gyakrabban okoz gondot, mint ahogy hasznot hoz. Ha mégis szeretnénk használni, akkor érdemes egy rövid egysoros kommentben jelezni, hogy itt most szándékosan használtuk a fall-through logic-ot.

Ha break utasításra fut rá a végrehajtás, akkor viszont megszakítja a switch utasításblokkjában lévő utasítások végrehajtását és kilép a switch-ből, és a switch utáni első utasítással folytatódik a program futása.

Switch expression a Java 12-ben

Holnap (2019. 03. 19.) jelenik meg a Java SE 12 verziója, ami egy új, mai igényeknek megfelelő switch kifejezést nyújt számunkra, bár igaz, hogy egyelőre csak kísérleti jelleggel. Ha ki szeretnénk próbálni, akkor engedélyeznünk kell az –enable-preview új parancssori opcióval mind fordításnál, mind futtatásnál.

Mi a különbség a switch statement és a switch expression között?

switch expression szemléltetése Drake-kelA switch statement önálló utasítás, amely ha végrehajtódik, akkor csak mellékhatása van, de nincs közvetlen eredménye.

Ezzel szemben a switch expression egy kifejezés, ami ha kiértékelődik, akkor egy eredményt is szolgáltat, ami lényegében az egész switch blokk helyére behelyettesítődik.

Talán az if-else és a feltételes hármas operátor (?:) a legjobb hasonlat erre. Míg az if-else-nél a kódblokkban megadva adhatunk meg utasításokat, amik az if feltételeként megadott kifejezés igaz / hamis volta alapján kerülnek lefuttatásra vagy kihagyásra, úgy a ?: operátornál a kérdőjel előtti kifejezés ha igaz, akkor a kérdőjel és a kettőspont közötti, míg a kérdőjel előtti kifejezés ha hamis, akkor a kettőspont utáni érték kerül behelyettesítésre az egész ?: kifejezés helyére.

Ugyanez a helyzet a switch kifejezésnél is. Nézzünk erre egy példát:

Példa switch expression-re

public class TestSwitchStatement {

    public static void main(String[] args) {
        int dayOfWeek = 3;
        String nameOfDay = switch (dayOfWeek) {
                   case 1 -> "hétfő";
                   case 2 -> "kedd";
                   case 3 -> "szerda";
                   case 4 -> "csütörtök";
                   case 5 -> "péntek";
                   case 6 -> "szombat";
                   case 7 -> "vasárnap";
                   default -> "ismeretlen";
                };
        System.out.println(nameOfDay);
    }

}

Több különbséget is megfigyelhetsz a switch statement-hez képest. A switch expression a fenti példában egy értékadás operátor jobb oldalaként szerepel, vagyis a nameOfDay változó értéke a dayOfWeek változó értéke alapján a neki megfelelő Stringet fogja megkapni. Ahogy láthatod, a lambda kifejezésekből is ismerős nyíl tokent (->) használhatjuk ezentúl a switch-en belül. Fontos különbség, hogy ha olyan case-t használunk, amit nyíl token követ, akkor nincs fall-through logic, vagyis ilyen esetben ezek a case-ek kizárják egymást. Vagy az egyik fut le, vagy a másik, de sosem lapolódnak át. Az persze előfordulhat, ha nincs default eset, hogy egyikre se illeszkedik a switch-ben megadott dayOfWeek változó értéke. A default esetet nem kötelező megadnunk, de ha nem adjuk meg, akkor minden esetet le kell fednünk case-ekkel a switch kifejezésünkben. Ha enumokról van szó, akkor végre a fordító képes ellenőrizni, hogy minden konstanst szerepeltettünk-e a case-eknél.

Hadd hívjam fel a figyelmed még egy apróságra: a switch expressionnél a switch utasításblokkjának végén van egy pontosvessző, hisz ez az, ami az egész utasítást lezárja. A switch statementek esetén a switch mögé nem kell pontosvesszőt tenni.

Bár a nyíl token segítségével elkerülhetjük a fall-through logic átláthatatlanságát, nem vagyunk rákényszerítve a használatára. Ugyanúgy használhatunk kettőspontot a case-ek felsorolásánál, és ekkor a switch statement-hez hasonlóan élhetünk ezzel a lehetőséggel. De vajon mi lesz ilyenkor a switch expression helyettesítési értéke?

Break értékkel

A switch expression-ökben egy újfajta break utasítást is használhatunk. Eddig címkézett és címkézetlen break vezérlésátadó utasításaink voltak csak, de a switch expression-ök esetén ez most kibővül egy új taggal, amikoris a break után egy értéket adunk meg. Ez nagyon hasonló a metódusok return utasításához, ahol a visszatérési értéket adhatjuk meg a return után. Pont ugyanez a szerepe a breaknek a switch expression-ön belül. Nézzünk erre is egy példát!

public class TestSwitchExpressionWithBreakReturnValue {

    public static void main(String[] args) {
        int dayOfWeek = 3;
        String nameOfDay = switch (dayOfWeek) {
                   case 1:
                       break "hétfő";
                   case 2:
                       break "kedd";
                   case 3:
                       break "szerda";
                   case 4:
                       break "csütörtök";
                   case 5:
                       break "péntek";
                   case 6:
                       break "szombat";
                   case 7:
                       break "vasárnap";
                   default:
                       break "ismeretlen";
                };
        System.out.println(nameOfDay);
    }

}

Ahogy láthatod, a fenti példában a break utasítás után egy Stringet adunk vissza eredményül és ez lesz a switch kifejezés eredménye, ez kerül behelyettesítésre az egész switch blokk helyére.

Egy case, több eset

Van még egy további funkció, amit az új switch szintaktika megenged számunkra. Korábban egy case pontosan egy esetet írhatott le, de ezentúl vesszővel elválasztva több összetartozó esetet is megadhatunk. Íme egy példa erre is:

public class TestSwitchStatementWithMultipleCases {

    public static void main(String[] args) {
        int dayOfWeek = 3;
        switch (dayOfWeek) {
        case 1, 2, 3, 4 ,5:
            System.out.println("hétköznap");
            break;
        case 6, 7:
            System.out.println("hétvége");
            break;
        }
    }

}

Finally

Fantasztikus új lehetőség vár ránk az elkövetkezendő Java verziókban. Egyelőre a switch expression még csak kipróbálás jelleggel van jelen a Java 12-ben, de várható, hogy későbbi verziókban élesítik majd. A kísérleti jellege miatt még elképzelhető, hogy változik a szintaktikája, ezért érdemes tájékozódni a production verziója megjelenése esetén. Tervezik a Java szoftverfejlesztő mérnökei, hogy mintaillesztéssel is kibővítik a switch-et, és akkor akár olyan case-eket is megadhatunk benne, hogy i < 42 és i >= 42.

Ha olvasnál még bővebben a switch expression-ökről, akkor a JEP 325 hivatalos dokumentációját ajánlom figyelmedbe.

Ha a Java legújabb verziói által nyújtott lehetőségek érdekelnek, akkor szerintem ez az előadásom is tetszeni fog neked. Kövess YouTube-on is!

Ha tetszett, oszd meg!

Szólj hozzá!