fbpx

Rekurzió és ciklus – mi a különbség?

Rekurzió és barátai

Programozás során mondhatni állandó jelleggel találkozunk azzal a szituációval, hogy valamilyen utasításokat újra és újra végre kell hajtani, hogy elérjük a kívánt eredményt. Épp ezért az imperatív programozási nyelvek egyik alapvető nyelvi eleme a ciklus (angolul loop) és a rekurzió, amik segítenek nekünk ilyen szituációkban. Nézzük először a ciklust! A ciklus szerepe többes.

Rekurzió szemléltetése - a notebook saját képét jeleníti meg
Rekurzió szemléltetése

Utasítások ismétlése

Fordítási időben ismert ismétlésszám esetén

Képzeld el először azt az egyszerű helyzetet, hogy ki szeretnéd íratni a konzolra azt az üzenetet, hogy „Ain’t nobody got time for that”! Ez egyszerű. Java-ban, így néz ki:

public class LoopDemo {

    public static void main(String[] args) {
        System.out.println("Ain't nobody got time for that");
    }

}

Ha most annyiban változik a feladatod, hogy nem egyszer, hanem 10-szer szeretnéd kiíratni, akkor ezt nyilván meg tudnád így valósítani:

public class LoopDemo {

    public static void main(String[] args) {
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
        System.out.println("Ain't nobody got time for that");
    }

}

Ez egy megoldás, de nem a legjobb több szempontból sem:

  • sokat kell gépelni
  • hosszú lesz a kész program forráskódja
  • nehéz rajta változtatni

Ha egyszerűen megismételted a kiíratás parancsot, mint a fenti megoldásnál, és később kiderül, hogy nem is pont ezt a szöveget kell kiíratni (és ez a valódi szoftver projekteknél is így van, hogy folyton változik az, hogy mit is kell a programnak csinálnia), akkor bajban vagy, mert mind a 10 sorban ki kell javítani. Például ha kell egy felkiáltójel is a sor végére, akkor mind a 10 sorban ki kell tenned:

public class LoopDemo {

    public static void main(String[] args) {
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
        System.out.println("Ain't nobody got time for that!");
    }

}

Sokkal egyszerűbb közölni a géppel, hogy van itt ez a parancs:

System.out.println("Ain't nobody got time for that!");

és ezt ismételd meg 10-szer! Ezt ciklussal így tudod megtenni:

public class LoopDemo {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println("Ain't nobody got time for that!");
        }
    }

}

Így sokkal tömörebb kódot kaptál. Ha nem 10-szer kell ismételni, hanem 100 000-szer (és van ilyen a gyakorlatban), akkor még sokkal kiélezettebb a helyzet és még nagyobbat nyersz azzal, hogy ezt a tömör formát használod.

Futási időben ismert ismétlésszám esetén

De van egy másik fontos helyzet, amikor a ciklusok nagyon jól jönnek és nélkülözhetetlenek. Ha már akkor biztosan tudod, hogy egy adott parancsot hányszor kell ismételtetni a géppel, amikor írod a programot (vagyis fordítási időben), akkor még nem is annyira kardinális kérdés, hogy ciklussal valósítod-e ezt meg, vagy nem. De ha időben sokkal később derül csak az ki, hogy egy parancsot hányszor kell ismételni (futási időben), konkrétan akkor, amikor a programodat valaki használja, vagyis futtatja, akkor nincs más lehetőséged, mint ciklust használni. Például a programodnak lehet, hogy úgy kell működnie, hogy amikor elindítja a felhasználó, akkor először be kell kérnie a programnak, hogy hányszor szeretné látni az üzenetet, és a felhasználó válasza alapján más és más eredményt fog adni:

import java.util.Scanner;

public class LoopDemo {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("Kérlek, adj meg egy számot: ");
        int times = in.nextInt();
        for (int i = 0; i < times; i++) {
            System.out.println("Ain't nobody got time for that!");
        }
    }

}

Azonban ciklikus utasítás végrehajtást nem csak ciklussal lehet elérni.

Faktoriális példa – ciklussal és rekurzióval

Vegyünk egy példát, a faktoriális számítást! Javaslom, hogy ez te is próbáld ki és programozd le saját gépeden, hisz nagy sikerélményt ad, ha meg tudod beszélni a gépeddel, hogy mit tegyen meg neked! Ehhez ezen az oldalon találod meg a fejlesztői környezet beállításának lépéseit. Kövesd végig az ottani lépéseket és térj vissza ide utána!

A faktoriális jele a matematikában a felkiáltójel. Csak pozitív egész számnak értelmezzük a faktoriálisát. Az 5 faktoriálisát például így írjuk le: 5!

Az 5! nem jelent mást, mint 5 * 4 * 3 * 2 * 1, vagyis igazából néhány szorzás műveletnek a rövidítése. Az 5! az előbbiek alapján 120. Ezt Java programmal is jól le tudod modellezni, hisz annyit kell tenned, hogy veszed a számot, aminek a faktoriálisát ki szeretnéd számolni és elkezdet összeszorozni a tőle eggyel kisebb számokkal, amíg el nem éred az 1-et. Amikor eléred az egyet, akkor kész vagy.

Megvalósítás ciklussal

Iterációval, vagyis hagyományos ciklussal ezt így tudod megvalósítani:

public class FactorialWithIteration {

    public static void main(String[] args) {
        int theNumber = 5;
        int result = factorial(theNumber);
        System.out.println("The factorial of " + theNumber + " is " + result + ".");
    }

    private static int factorial(int theNumber) {
        int result = 1;
        for (int i = theNumber; i > 0; i--) {
            result *= i;
        }
        return result;
    }

}

Megvalósítás rekurzióval

A rekurziónál nem kell egyik ciklust sem használnod, ami a Java nyelvben elérhető, hisz az utasítások ismételt végrehajtását azzal is elérheted, hogy egy metódust önmagából újra meghívsz:

public class FactorialWithRecursion {

    public static void main(String[] args) {
        int theNumber = 5;
        int result = factorial(theNumber);
        System.out.println("The factorial of " + theNumber + " is " + result + ".");
    }

    private static int factorial(int theNumber) {
        if (theNumber == 1) {
            return 1;
        }
        return theNumber * factorial(theNumber - 1);
    }

}

Az 5! az nem más, mint 5 * 4!

A 4! az nem más, mint 4 * 3!

A 3! az nem más, mint 3 * 2!

A 2! az nem más, mint 2 * 1!

Az 1! az 1.

Lényegében ezt a logikát valósítottad meg a fenti kódban.

Ciklus és rekurzió összehasonlítása

Hasonlóságok rekurzió és ciklus között

Közös a két módszerben, hogy mindkettővel utasításokat tudunk ismételtetni a Java virtuális géppel, vagyis nem kell kiírnunk többször ugyanazt a parancsot csak azért, hogy újra lefuttassa, hanem elég egyszer. A faktoriális esetén ez a művelet a szorzás. Ha megfigyeled, akkor maga a szorzás művelet * csak egyszer szerepel a forráskódban.

Közös még az is a két módszerben, hogy mindkettőnél van egy feltétel, ami szabályozza, hogy mikor kell az ismétlést abbahagyni. A ciklusnál ezt ciklusban maradási feltételnek hívjuk, a rekurziónál pedig tipikusan kilépési feltételnek. Ha nem lenne ilyen feltételünk, akkor az ismétlés a végtelenségig zajlana, az adott utasításokat újra és újra végrehajtaná a gép, és sose érne véget. Ez működésképtelen programhoz vezetne.

Különbségek rekurzió és ciklus között

Különbség a két módszer között a végrehajtás menete. A rekurziónál egy metódus önmagát hívja meg újra meg újra. Ennek van némi overhead-je a Java virtuális gép szintjén, mind processzorhasználat, mind memóriahasználat terén. A metódushívások láncolatát a JVM egy elkülönített memóriaterületen, a stack-en (verem) tárolja, aminek limitált a mérete. Ha ez megtelik, akkor StackOverflowError-t kapunk, ami egy komoly hiba, és ami az adott végrehajtási szál azonnali leálláshoz vezet. Ez egy nagy alkalmazás esetén elfogadhatatlan.

A ciklus esetén a fent említett overhead nem lép fel, így hatékonyabb program végrehajtáshoz vezet a használata.

Lényeges különbség még a két módszer között az, hogy vannak olyan problémák, amik rekurzióval sokkal tömörebben megfogalmazhatók, és ha ezt használod, akkor maga a forráskód rövidebb lesz, míg ha iterációval írnád, akkor hosszabb kódot kapnál. Ezt az adott szoftverfejlesztési projekt függvényében tudod eldönteni, hogy megéri-e a rekurzív megvalósítással élned.

Összefoglaló

Összességében elmondható, hogy utasítások ismétlésére nem csak ciklusok használhatók, hanem önmagukat meghívó úgynevezett rekurzív metódusok is. Különös figyelemmel kell azonban lenned, amikor ilyen szerkezetet használsz, hisz ha az ismételt végrehajtásnak a kilépési feltételét nem jól adod meg, akkor az hibás program működéshez vezethet. Ez rekurzió esetén StackOverflowError-hoz vezet míg ciklusok esetén végtelen ciklushoz, vagyis a programod egy adott utasításblokkot fog újra és újra végrehajtani anélkül, hogy onnan valaha tovább tudna menni. Ha persze a cikluson belül valamilyen memóriafoglalás is történik, akkor OutOfMemoryError-ra fog futni a programod. Ezen szerkezetek használatakor mindig különös figyelemmel kell eljárnod.

Ha szeretnéd gyakorolni a rekurziót további Java példákon keresztül, akkor ajánlom figyelmedbe a CodingBat oldalt, ahol több apró példán keresztül elsajátíthatod ennek fortélyait.

Java 13 újdonságai – szöveges blokk

A szöveges blokk és a switch kifejezések lesznek talán a programozók mindennapjait közvetlenül befolyásoló új nyelvi eszköz a jövőben. De mik is ezek pontosan és hogy lehet ezeket használni? Milyen más újításokat vezet be a Java 13? Olvasd el lentebb!

Tartalom

  1. Mikor lesz elérhető a Java 13?
  2. Szöveges blokk – Text blocks (preview)
    1. Miért jó a szöveges blokk?
    2. Hogy néz ki a szöveges blokk?
  3. Switch kifejezések – Switch expressions (preview)
  4. ZGC: Nem használt memória felszabadítása az operációs rendszer számára
  5. Régi Socket API újraimplementálása
  6. Dinamikus CDS archívumok
  7. Összefoglalás

Mikor lesz elérhető a Java 13?

A végleges verzió várhatóan 2019. szeptember 17-én fog megjelenni. Néhány új nyelvi elem csak kísérleti jelleggel kerül bele a Java-ba, ezeket ha ki szeretnéd próbálni, akkor egy külön kapcsolóval kell ellátni a javac és java parancsokat. Részletesebben lásd lentebb!

De nem kell eddig várnod, hogy kísérletezz ezekkel az új feature-ökkel, hisz a GA (general availability) előtt is letölthetők a tesztelhető verziók.

Szöveges blokkok – Text blocks (preview)

A Java 13-mal érkezik ez a új feature, de egyelőre csak preview formában, vagyis még csak kísérleti jelleggel. Ha ki szeretnéd próbálni, akkor a programod fordításakor és indításakor a JVM-nek át kell adni a

--enable-preview

kapcsolót. Ennek hiányában a fordító bután fog rád nézni a forráskódod láttán.

Ha nem parancssorból futtatod a programod, akkor a kedvenc IDE-d legújabb verziójában a beállításoknál kell ezt engedélyezned. Eclipse esetén Window -> Preferences -> Java -> Compiler -> Enable preview features

szöveges blokk használatához szükséges beállítás Eclipse-ben
Preview feature bekapcsolása Eclipse-ben

Már a Java 12-be is terveztek egy raw string nevű feature-t, ami végül nem került bele, a kiadás előtti utolsó pillanatban kiszedték belőle. Ezek a nyers string-ek is hasonló ötlet alapján születtek, de végül a Java fejlesztő mérnökei úgy gondolták, hogy egy másik megközelítés lenne a megfelelőbb hosszú távon. A raw string, ahogy a nevük is utal rá, nyers szöveges literálok lettek volna, vagyis a kezdő és bezáró idézőjelei között nem értelmezte volna a JVM a speciális karaktereket.

Újragondolták ezt a megközelítést és a raw stringek helyett a text blokkok bevezetése mellett döntöttek a fejlesztők.

Miért jó a szöveges blokk?

A probléma, amit a szöveges blokk hivatott megoldani onnan fakad, hogy Java forráskódban sokszor kell más programozási nyelvek kódjait összeállítanunk. Gondolok itt például az adatbázis kezelésnél használatos SQL-re, a webes alkalmazásoknál a front-endnek küldendő JSON fájlokra, de akár XML-eknél és HTML-eknél is gondokba ütközünk. A problémát az okozza, hogy más programozási nyelv is használ olyan írásjeleket, speciális karaktereket, amik a Java számára is jelentőséggel bírnak. Például az idézőjel (“) a Java-ban a String literálok elejét és végét jelzi. Ha a String literál tartalmaz egy idézőjelet valahol a tartalmán belül, akkor ezt escape-elni kell egy backslash (\) szimbólummal, vagyis \” -t kell írnunk a kódunkba. Amikor HTML kódot Java-ban String-ként kezelünk, akkor olyan gyakran kell élnünk ezzel az escape-eléssel, hogy teljesen olvashatatlanná teszi a kódot. Gondot okoznak az új sor karakterek is, mert a Java-ban egy sima String literált egy sorban kell definiálni. Az új sor karaktert speciális \n escape szekvenciával kell jelölnünk, hogy belekerüljön a String literálunkba.

Hogy néz ki a szöveges blokk?

3 idézőjellel kell kezdődnie a szöveges blokknak és ezzel is kell záródnia.

String text = """        
                Az erős fókuszálású szinkrotronokban
                       rengeteg mágnes található, hogy fókuszálják a részecskenyalábot
                              és meggörbítsék a pályáját.              
        """

Fontos, hogy egy előfeldolgozáson esik át az ilyen módon megadott String literál, az első karaktere ennek a String-nek a nagy A betű lesz a mondat elején, holott egy csomó szóköz (whitespace) karakter megelőzi azt. Ezek a kezdeti whitespace-ek eldobásra kerülnek, egyfajta automatikus trim-elés történik. Ugyanez a helyzet a String literál végén is, az ottani szóközök is levágásra kerülnek. A mondat végén a pont után lesz egy sortörés. Ha ezt nem szeretnénk, akkor így kell megadnunk az előző szöveg blokkot:

String text = """        
                Az erős fókuszálású szinkrotronokban
                       rengeteg mágnes található, hogy fókuszálják a részecskenyalábot
                              és meggörbítsék a pályáját."""

Switch kifejezések – Switch expressions (preview)

A switch kifejezések már a Java 12-ben is kipróbálhatók voltak, erről írtam is egy blog posztot annak idején, aminek az átolvasását jó szívvel ajánlom továbbra is, hisz ez az egyik legnagyobb változás a Java nyelv eszközkészletét tekintve mostanság és a közeljövőben, ami a mindennapi programozás során előkerül és így használható. Az blog posztban leírtakhoz képest egy apró változás történt pusztán azóta. Amikor expression-ként, vagyis kifejezésként használjuk a switch-et, akkor a Java 12-s preview-ban még a break utasítást használhattuk egy értékkel együtt, hogy az adott switch kifejezés az adott értékkel térjen vissza mint helyettesítési értékkel, azonban a Java készítői úgy gondolták, hogy jobb lenne egy másik kulcsszót használni. Így ebben az esetben a break helyett a yield szót tudjuk majd használni. Persze a Java 13-ban is csak preview jelleggel került be ez az új feature, ezért ez még tovább változhat a következő verziókban, amíg nem véglegesítik.

String nameOfDay = "szombat";
int dayOfWeek = switch (nameOfDay) {
    case "hétfő": 
        yield 1;
    case "kedd":
        yield 2;
    case "szerda":
        yield 3;
    case "csütörtök":
        yield 4;
    case "péntek":
        yield 5;
    case "szombat":
        yield 6;
    case "vasárnap":
        yield 7;
    default:
        System.out.println("Ismeretlen nap.");
        yield 0;
};
System.out.println("Ez a(z) " + dayOfWeek + ". napja a hétnek.");

A yield egy új kulcsszó lesz a Java 13-tól kezdve.

ZGC: Nem használt memória felszabadítása az operációs rendszer számára

A Java 11 egyik kísérleti jelleggel bevezetett új szemétgyűjtője volt a Z Garbage Collector, vagy röviden ZGC. A több terabájtos heap-ek esetén is nagyon rövid megállási időket ígér. Az egyik hátránya az volt, hogy ha egy program sok memóriát igényelt a futása során, akkor a megnövekedett heap használat még ha az idővel meg is szűnt, akkor se tudta visszaadni a most már újra felszabadult memóriaterületet az operációs rendszernek. A Java 13-ban ezt a funkciót implementálták a ZGC esetén is.

Java 9-től kezdve az alapértelmezett garbage collector továbbra is a G1GC, vagyis a Garbage First Garbage Collector.

Régi Socket API újraimplementálása

A hálózati kommunikációt lehetővé tevő java.net csomagban található Socket és ServerSocket osztályok implementációi még a JDK 1.0-ban történtek, vagyis 1996-ban. Ezek feje felett igencsak eljárt már az idő, és több problémát is okoznak. Az eredeti implementációk régi Java és C kód keveréke, amit nehéz karbantartani és nehéz bennük hibát keresni.

Angolul itt tudsz erről többet olvasni, ha kíváncsi vagy a részletekre: JEP 353

Dinamikus CDS archívumok

A CDS, vagyis a Class Data Sharing, egy olyan már most is létező lehetősége a JVM-nek, ami az indítási idejét tudja felgyorsítani a programunknak, illetve memóriahasználat szempontjából is hasznos tud lenni. Egy alkalmazás osztályait lehet archiválni annak leállásakor, és így a futása alatt a class loader-ek által szerzett információt össze lehet gyűjteni egy archívumba, aminek a segítségével a következő indítás teljesítménye javítható. A Java 13-as újítás e téren segít ezen információk kigyűjtésében és nem lesz többé szükséges próbafuttatások végzése, mint korábban.

Összefoglalás

A Java 13-mal ez az 5 új feature kerül bele a nyelvbe, de ebből 2 még csak kísérleti jellegű, preview feature. Ezeket csak akkor tudod kipróbálni, ha egy külön kapcsolóval bekapcsolod a javac és a java programoknál, vagy az IDE-dben. A switch kifejezés a Java 12 verziója óta keveset változott, de még mindig ez az egyik olyan új lehetőség, amit a legtöbbet tudjuk majd a napi programozási feladataink során használni. A másik nagy újítás a text block, vagyis a szöveges blokk, ami egy igen rég óta várt funkció. A többsoros és speciális karaktereket tartalmazó String literálok beágyazása a forráskódunkba olvasható módon fantasztikus újítás. Mindezeket kipróbálhatod a végleges verzió várható szeptemberi megjelenése előtt.

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!

A webfejlesztés folyamata lépésről lépésre

Egy építkezés projekthez hasonlóan a webfejlesztés is meghatározott lépések egymásutániságából felépülő folyamat, ahol maga a programozás önmagában nem elégséges a siker szempontjából.

Legyen szó egy weboldal vagy webes alkalmazás elkészítéséről, a programozást megelőző és azt követő lépések kulcsfontosságúak a teljes projekt sikere szempontjából. Éppen ezért ebben a cikkünkben megpróbálom összeszedni azokat a lépéseket és jellemzőiket, amelyek általánosságban minden webfejlesztés projektre érvényesek.

Íme a lépések, amelyeket az alábbiakban részletezni fogok:

  1. Információ gyűjtés
  2. Kutatás és tervezés
  3. Design
  4. Programozás
  5. Tesztelés és élesítés
  6. Karbantartás és üzemeltetés

1. Információ gyűjtés: okok, célok és célközönség meghatározása

Sok más projekthez hasonlóan a webfejlesztés első lépése a szükséges információk begyűjtése. A projekt elején tisztázni kell a fejlesztés mögött húzódó okokat, üzleti célokat, a célközönséget és a tulajdonosok elvárásait, valamint jó tisztába lenni a marketing által támasztott elvárásokkal is.

  • Mi a webes projekt célja?
  • Mi a célközönség? Kiknek készül a digitális termék?
  • Milyen tartalommal bírjon a termék?

A weboldal, mint digitális termék célja, célközönsége és a webhelyen található információk alapvetően befolyásolják, hogy milyen technológiára lesz szükség és milyen funkcionalitással bírjon a weboldal a befejezést követően.

Mi a weboldal fejlesztésének a célja? Értékesítés, brand építés, termék- vagy szolgáltatás népszerűsítés, szakértői szerep kiépítése? Egy mosógép értékesítéssel foglalkozó weboldal felépítése, tartalma, funkcionalitása nagymértékben különbözik egy céges weboldalétól, éppen ezért fontos minél több információt begyűjteni ebben az első fázisban.

Az előbb felsorolt információk ahhoz is szükségesek, hogy a webfejlesztők az igények alapján egy specifikációt tudjanak készíteni, amelynek jóváhagyása után egy árajánlat illetve egy szerződés készül, amelyben többek között a fejlesztési mérföldköveket és a határidőt is rögzítik.

A befejezetlen, elhaló projektek mögött sok esetben az húzódik, hogy ebben a szakaszban nem lettek az igények és elvárások pontosítva, írásban rögzítve. Így értelemszerűen a végeredmény is különbözött attól, mint amit a megrendelő elképzelt.

webfejlesztés terve

2. Kutatás és tervezés: sitemap és drótváz készítés

A kutatási szakaszban információkat gyűjtünk és összeállítjuk a weboldal tartalmi hierarchiáját és webhely térképét.

A webhely térkép (sitemap) bemutatja a weboldal főbb tartalmi egységeit és azok hierarchiáját. A sitemap tervezése során figyelembe kell venni a könnyű navigációt, a logikailag összetartozó tartalmakat illetve a célcsoport igényeit is. Ideális esetben a látogató minden őt érdeklő tartalmat kevés számú kattintással el tud érni és a navigáció lehetővé teszi, hogy az igényeinek megfelelő sorrendben tudja bejárni a weboldalt. A bonyolultabb szerkezetű oldalak tervezése során olyan technikák segítenek, mint például a card sorting, amely segít megtalálni az ideális csoportosítást a menüpontok között.

A drótváz (wireframe) fekete-fehér rajzban jeleníti meg a weboldal nyitólapjának és főbb menüpontjainak a struktúráját, elrendezését. A fekete-fehér legtöbbször szabadkézi rajz lehetővé teszi, hogy viszonylag gyorsan többfajta variációt is tesztelni lehessen a célközönségen és a módosításokat gyorsan át lehessen vezetni a terven.

A felhasználói élmény alapú tervezés az utóbbi években kezd egyre nagyobb teret nyerni, amely magával hozta azt is, hogy erre több webfejlesztés projeknél látom, hogy a grafikai tervezést megelőzi a drótváz készítés, amely összességében nemcsak felgyorsítja a tervezési folyamatot, de azt is biztosítja, hogy a végső grafikai tervek egy hatékonyság szempontjából optimalizált weboldalt ábrázoljanak.

3. Design

Ebben a szakaszban a grafikusoké a főszerep. A kutatás során nyert adatokat folyamatosan szem előtt tartva a drótvázakból végleges grafikai tervet készítenek.

A grafikai tervezés ennél azért sokkal összetettebb folyamat. Ideális esetben készülnek drótvázak, tehát a website egyes oldalainak szerkezete, elrendezése már jóvá van hagyva, de az arculat, a színek, a betűtípusok, a képek, az illusztrációk, ikonok és egyéb formák kiválasztása most történik meg. Mindezt úgy kell megalkotni, hogy a weboldal a későbbiekben probléma nélkül leprogramozható legyen és a célközönség számára is egy tetszetős, harmonikus végeredmény szülessen.

A grafikai tervezés részét képezi az alkalmazott betűtípusok, ikonok, színek valamint grafikai alapelemek kigyűjtése, amely megkönnyíti a programozó munkáját és biztosítja, hogy a fejlesztés során maradéktalanul betartásra kerüljön az az arculat, amelyet megalkottak.

4. Webfejlesztés programozás része

A design szakaszban elkészült tervek és arculat felhasználásával a programozók egy működőképes weboldalt (webes alkalmazást) alkotnak. Az eddig statikus képként megjelenő design-ból egy kattintható, dinamikus website lesz. A frontend, vagyis a látogató számára böngészőben megjelenő felület mögé egy adminisztrációs felület, illetve ahogy a legtöbb esetben nevezik tartalomkezelő rendszer kerül, amely lehetővé teszi a weboldal tartalmának frissítését, szerkesztését, bővítését.

A webfejlesztő (programozó) feladata az oldal funkcionalitásának kialakítása mellett annak biztosítása, hogy ….

  • az oldal minden megjelenítő eszközön tökéletesen működjön, vagyis a reszponzivitás kihívásainak megfeleljen
  • az oldal a tartalomkezelő rendszerből frissíthető, módosítható legyen. A leggyakrabban módosításra szoruló részek szerkesztéséhez ne kelljen a webfejlesztő segítségét igénybe venni.
  • a weboldal kódolása áttekinthető legyen és szükség esetén más fejlesztő által átvehető, továbbfejleszthető legyen
  • a weboldal legnépszerűbb böngészőkben hibamentesen jelenjen meg és működjön
  • a weboldal úgy legyen leprogramozva, hogy a lehető leggyorsabban beöltődjön, mert a betöltés sebessége kritikus a felhasználói élmény szempontjából
  • a weboldal forráskódjába kerüljön bele az összes olyan követőkód, amely az online marketing folyamat működtetését és mérését lehetővé teszi
  • a weboldal keresőbarát módon legyen kialakítva, megfeleljen a Google irányelveinek
  • a weboldal biztonságos legyen, fel legyen készítve a webről érkező támadások elhárítására
  • a weboldal szükség esetén támogassa a többnyelvűséget

A weboldal tartalmának első feltöltését is általában a programozók vagy operátorok szokták elvégezni, mert itt még kisebb-nagyobb módosításokra van szükség a konkrét információk birtokában.

5. Tesztelés és élesítés

Ha készen van a weboldal és funkcionálisan és tartalmilag is megfelelő, akkor jön a tesztelés. Az élesítés előtt még egyszer le kell ellenőrizni a böngésző kompatibilitást, a betöltődési sebességet, a nyelvi verziók helyességét, a hibaüzeneteket. El kell végezni a régi weboldal megfelelő oldalainak átirányítását az újakra. Ki kell tölteni a SEO szempontjából fontos META adatokat, le kell ellenőrizni a technikai és on-page SEO faktorokat. A közösségi média és analitikai, valamint hirdetési kódokat át kell rakni a régi weboldalból az újba.

A weboldal a végleges tárhelyre kerül és a domain név beállítását követően élesben kezd működni.

6. Karbantartás és üzemeltetés

A weboldal élesítése után a kapcsolat nem szakad meg a webfejlesztő és az ügyfél között. A weboldalak rögtön az indítás utána a web felől számtalan támadásnak vannak kitéve, ezért folyamatos kontrollt, ellenőrzést igényelnek. Tartalomkezelő rendszereiket folyamatosan frissíteni kell annak érdekében, hogy ellenállók legyenek a webről jövő támadásoknak.

Zárszó

A webfejlesztés mint projekt már régóta nem egyszemélyes feladat. Az utóbbi években olyan sok önálló szakma fejlődött ki belőle, hogy egy személy képtelen mindegyiknek egyszerre megfelelni. Egy közepes webfejlesztés projekten is projektvezető, kutatók, UX designerek, programozók, operátorok, SEO és online marketing szakemberek dolgoznak közösen, csak így biztosítható, hogy a végeredmény éles piaci környezetben is megállja a helyét.

Szerző: Tusnádi István

Clever-Commit – Programozókat segítő mesterséges intelligencia

A Clever-Commit egy új fejlesztésű szoftver, ami megváltoztathatja a programozók napirendjét, névlegesen várhatóan kevesebb időt kell majd a hibajavítással töltenünk.

Programozóként nap mint nap módosítgatjuk fő projektünk kódbázisát. Bár időnk nagyobb részét a kód olvasásával töltjük, miután 2 napot töltöttünk kollégánk által írt kód megfejtésével, csak ráfanyalodunk a forráskód módosítására is.

Van, aki bátrabb, van, aki óvatosabb, de végül a kód módosítva lesz, elvégre ezért fizetnek minket. Verziókezelő rendszert még a kisebb projektek esetén is érdemes használni. Jobb helyzetben a fejlesztői csapat programozói gyakorlatának része, hogy mielőtt commit-álják az új feature-höz tartozó módosításokat, egy másik fejlesztővel code review-t tartanak, átnézik egymás kódját. Ez egy nagyon jó gyakorlat, még kisebb commit-ok esetén is, hisz sok hiba kiszűrhető vele.

Előfordulhat azonban, hogy mindkét fejlesztő figyelmét elkerüli egy hiba. Ekkor jön jól a Ubisoft új mesterséges intelligenciája, amit Clever-Commit-nak neveztek el. Igazából már tavaly márciusában debütált ez az új technológia, de akkor még Commit-Assistant néven. Egy igen látványos videóval hozták a nagyvilág tudomására ennek az új segédeszköz létrejöttének hírét:

Hogyan működik?

A Ubisoft egy nagy játékfejlesztő cég, aki nagy költségvetésű videójátékokat fejleszt. Az ilyen szoftverprojekteken sok fős csapatok dolgoznak, sok különféle szerepben. Magát a kódbázist naponta több tíz programozó módosítja. A munkájukat a verziókezelő rendszerrel jól össze tudják hangolni, de az általuk írt kód bizony hibákat tartalmazhat. Még az is lehet, hogy külön-külön helyesen működne az általuk írt kód, de két fejlesztő kódja összeakad és ez okoz hibát. Sajnos vannak olyan alattomos, mélyen megbúvó hibák, amik csak később, lehet, hogy csak a program hivatalos kiadása után kerülnek felszínre, amikor már több ezer ember megvette azt a boltban és játszani kezdett vele. Ezeket tipikusan jelentik a játékosok a fejlesztőknek, akik kijavítják azokat és patch-ek formájában el is juttatják a játékosoknak. Azonban ez nem vet túl jó fényt a cégre, hogy bug-os a programjuk. A játékélményt teljesen tönkreteheti egy ilyen húzás.

Tesztelés

Clever-Commit - a Ubisoft és Mozilla összefogásából származó újdonság

Természetesen a kód gondos átvizsgálásával, tesztelésével az ilyen hibák még időben kideríthetők, azonban ez jelentős idő – és ezzel pénz – befektetést jelent a szoftvergyártó cégnek.

Itt jön képbe a Clever-Commit, ami a fejlesztők által végzett módosításokat átvizsgálja a verziókövető rendszerbe történő commit-álás előtt, és jelzi, ha észlel valami hibát. A leggyakoribb programozói hibákra már eddig is voltak statikus (sőt, akár dinamikus) kódelemző eszközök, azonban amiben újat tud mutatni a Clever-Commit, az az, hogy a fejlesztők által commit-olt változtatások elemzésével folyamatosan tanul, finomhangolja a heurisztikáját, így a szoftverprojekt előrehaladásával egyre okosabb lesz és így tényleg akkor tud jelezni, amikor van valami javítanivaló.

Mennyit segít?

A Ubisoft eredeti becslése szerint 20%-ot spórolhat a fejlesztők idejéből, amit amúgy hibajavítással töltenének. Van azonban, hogy hamis riasztásokat ad a program. Mivel minták alapján dolgozik, lehetséges, hogy az egyik fajta hibára jellemző minta – véletlen – hasonlít egy amúgy helyes kódnak a mintájára. Ilyenkor egy figyelmeztetést jelenít meg a fejlesztőnek, akinek a kód átnézésével magának kell eldöntenie, hogy helyes vagy bug-os kóddal áll szemben. Ezek a hamis riasztások persze megint a fejlesztők idejéből vesznek el, ezért érdemes ezeket a minimálisra csökkenteni.

Szerencsére a Clever-Commit-ot úgy alakították ki, hogy tanul az ilyen hibáiból és ezért az idő előrehaladtával lassan de biztosan csökken a téves figyelmeztetések száma.

A Clever-Commit felhasználása

A Ubisoft összefogott a Mozillával, így a Firefox fejlesztési ciklusának egyik részénél bevezetik hamarosan ezt az új technológiát, ami segít kiszűrni majd a böngésző kódbázisában a hibákat.

Sylvestre Ledru – a Firefox kiadásának és minőségi menedzsmentjének vezetője – azt nyilatkozta, hogy izgalmas a Ubisoft projektje és szívesen járulnak hozzá a Clever-Commit továbbfejlesztéséhez.

Összefoglalás

A kódminőséget automatikusan ellenőrző szoftverek egyre szélesebb körben kerülnek alkalmazásra, ahogy a szoftverfejlesztő cégek vezetői felismerik, hogy ez az előnyüket szolgálja.

Bár a Clever-Commit nem egy ingyenes, nyílt forráskódú szoftver, jelenleg is elérhetők hasonló, ingyenes megoldások a piacon. Igaz, nem adaptívak, de sokat segítenek a programozók mindennapjain. Én mindenképp javaslom a Sonarlint (SonarQube) kipróbálását, ami a kedvenc IDE-nkkel összeköthető és valós időben ad nekünk visszajelzést az esetlegesen elkövetett baklövéseinkről.

Amíg az általunk írt kód minőséget a kódelemző eszközökkel növelhetjük, addig a megírt kód mennyiségét, vagyis produktivitásunkat a gyorsbillentyűk használatával.

Bug mentes kódot kívánok mindenkinek! 🙂

Eclipse gyorsbillentyűk – produktivitási tippek Java fejlesztőknek

Bevezető gondolatok

Az Eclipse gyorsbillentyűk a segítségedre lesznek!

Rohanó világban élünk, szokták mondani. Az üzleti szférában sokszor az nyer, aki gyorsabb, aki hamarabb képes piacra dobni egy terméket. A különbféle kütyük nagyban tudnak segíteni nekünk, hogy gyorsabbak legyünk. Gondolj csak a mosógépre, mosogatógépre, porszívóra, de akár a telefonra vagy a számítógépre! Elvégeznek olyan munkákat, amiket ha nekünk saját kezűleg kéne, akkor tovább tartana. Időt spórolnak nekünk, így több időnk marad más, számunkra fontosabb dolgokra.

Ez a helyzet az informatikai eszközök esetén is fennáll. A számítógép egy ezen kütyük közül, ami segít nekünk munkánkban, kikapcsolódásunkban. Persze tudni kell rendesen kezelni. Míg egy porszívó kezelése viszonylag triviális dolog, a számítógép jóval összetettebb, így több a hibalehetőség is. Biztos vagyok benne, hogy te is tapasztaltad már, hogy bár az életünk segítése a számítógép célja, mégis néha inkább megnehezíti azt: túl lassú, nem azt teszi, amit szeretnénk, kék halált hal, General Protection Fault, stb.

A helyzet reménytelen, de nem súlyos.

Mit tehetünk?

Érdemes minél jobban megismerkednünk az eszközzel, amit használunk. Hiába van nálunk egy svájci bicska, ha fafűrészeléshez a kést használjuk. Ugyanez a helyzet a programozásban is fennáll. Az Eclipse – ahogy a többi IDE – feladata, hogy megkönnyítse életünket, könnyebbé, hatékonyabbá tegye a szoftverfejlesztést. De hiába áll rendelkezésünkre az az ezernyi kis segítség, ha nem is tudunk a létezésükről és ezért nem használjuk.

A kódminőség fontos

Összetett fogalom a hatékonyság, ezernyi tényező beleszámít, de lényegében egy sebességszerű fogalomról beszélhetünk. A sebesség nem más, mint adott idő alatt megtett út. Fontos persze, hogy először kitűzzük, hogy hova is megyünk, mert különben ha rossz irányba tesszük meg az utat, az mit sem segít hosszú távon. Így van ez a programozásban is. A cél, és egyben az egyik legnagyobb kihívás manapság a programozói szakmában, az a minőségi szoftver elkészítése gyorsan. Ellentmondásosnak hangzik, pedig nem az, csak rendszerben kell szemlélnünk a dolgot. Ahogy az algoritmuselméletből is tudhatod, nem mindig a mohó algoritmus vezet a globális optimumhoz. Ha az a cél, hogy a világ legmagasabb pontját elérjük, akkor általában nem jó megoldás azt a taktikát követni, hogy mindig menjünk arra, amerre a legjobban emelkedik az út. Ez a mohó megközelítés – jelen helyzetemben – a Gellért-hegyre (235 m) vinne, ami bár lokális optimum, mégis mily messze van a globális optimumtól, a Csomolungma (8 848 m):

A célt akkor hát tudjuk: minőségi szoftvert produkálni minél hamarabb. Két fontos tényezője van: 1. minőségi, 2. gyorsaság. Mindkettőt mérhetővé lehet tenni. Angolul az előbbit code quality-nek, utóbbit productivity-nek hívják.

A kódminőséget az ISO/IEC 25010:2011 szabvány írja le. Egy kód akkor számít jónak, ha eleget tesz a specifikációnak, könnyen olvasható és ezáltal módosítható, kiegészíthető, könnyen karbantartható. Ezt magasszintű programozási ismeretekkel, best practice-ek ésszerű követésével lehet leginkább elérni, sokat számít a szakmai tapasztalat ebben. Ennek fejlesztéséhez kitartó tanulásra lesz szükséged.

A hatékonyság növelése

A produktivitást ennél jóval könnyebb növelni, ha megismered a rendelkezésre álló eszközeidet és azok képességeit. Legjobb barátod a nagybetűs Fogyatkozás, avagy az Eclipse. Igen, a másik barátod a Google, de őt most kicsit tegyük félre! Ebben a pillanatban jelent meg (tényleg!) az Eclipse következő verziója az Eclipse Photon, amivel kapcsolatban szeretnék megosztani veled néhány újdonságot, illetve általános, Eclipse-szel kapcsolatos jótanácsokkal is szeretnélek ellátni, amik nekem személy szerint sokat segítenek a mindennapi programozói munkám során.

Eclipse gyorsbillentyűk

A legnagyobb eredményt az úgynevezett gyorsbillentyűk (hot key vagy más néven keyboard shortcut) megismerésével tudod elérni, aminek bár eleinte van egy kis tanulási görbéje, bőven megtérül. Ahogy az angol elnevezés jól mutatja, egy shortcut-ról, vagyis egy rövidebb útról van szó, ami hamarabb célba juttat. Az általános kijelölés, másolás, beillesztés lépéssorozatot biztos vagyok benne, hogy te is ismered, ráadásul ennek kivitelezéséhez az univerzális Ctrl + C és Ctrl + V gyorsbillentyűket is valószínűleg használod.

A következő táblázatban összefoglaltam a tapasztalt programozók által leggyakrabban használt billentyűkombinációkat. A billentyűparancsokat balról jobbra olvasva tudod te is kivitelezni, a pluszjel a gombok egyszerre történő lenyomását jelenti. Némely ezek közül más alkalmazásokban is használható, főleg a kurzor navigációjával és a kijelölésekkel kapcsolatosak.

Gyorsbillentyű Hatása
nyílbillentyűk Kurzor navigálása egy karakterrel az aktuális pozícióból kiindulva a nyíl irányába.
Ctrl + C Előzetesen kijelölt szöveg másolása a vágólapra a memóriában.
Ctrl + V A kurzor aktuális pozíciójához a vágólap tartalmának a beillesztése.
Shift + nyílbillentyűk A kurzor aktuális állásától a nyílbillentyű irányába szöveg kijelölése.

A le és fel irányba történő kijelölés egész sorokat jelöl ki.

Ctrl + nyílbillentyűk Kurzor navigálása az adott irányba egy szövegegységgel.
Ctrl + Shift + nyílbillentyűk Kijelölés a kurzor aktuális helyzetétől szövegegységekben a lenyomott nyílbillentyű irányába.
Ctrl + A Mindent kijelöl az aktuális ablakban.
Ctrl + S Az aktív szerkesztő ablak módosult tartalmának mentése.
Ctrl + Shift + S Az összes megnyitott szerkesztőablak módosult tartalmának mentése.
Ctrl + szóköz A kurzor aktuális pozícióján tartalomkiegészítés kérése. Az adott ponton releváns szövegrészeket ajánl fel.
Ctrl + Alt + fel- vagy le nyílbillentyű Az aktuális sor másolása az aktuális sor felé vagy alá, ha több sor is ki volt jelölve, akkor az összes kijelölt sor másolása a megadott irányba.
Alt + fel- vagy le nyílbillentyű Az aktuális sor mozgatása felfelé vagy lefelé, ha több sor is ki volt jelölve, akkor az összes kijelölt sor mozgatása a megadott irányba.
Ctrl + Shift + X A kijelölt szöveg csupa nagybetűssé konvertálása.
Ctrl + Shift + Y A kijelölt szöveg csupa kisbetűssé konvertálása.
Ctrl + Shift + C Az aktuális sor ki- vagy visszakommentezése, ha több sor is ki volt jelölve, akkor az összes kijelölt sor kommentezésének váltása.
Ctrl + Shift + R Fájl megnyitása tetszőleges workspace helyről, annak nevének egy részének megadásával.
Ctrl + Shift + T Típus megnyitása tetszőleges workspace helyről, annak nevének egy részének megadásával.
Alt + Shift + R A kurzor pozícióján található típus, metódus vagy változó átnevezése annak minden hivatkozásával együtt.
Alt + Shift + L A kijelölt kódrészlet kiemelése egy új helyi változóba, a változó nevét a felugró ablakban adhatjuk meg.
Alt + Shift + M A kijelölt kódrészlet kiemelése egy új metódusba, a metódus nevét a felugró ablakban adhatjuk meg.
Ctrl + O Kivonat az aktuálisan megnyitott típus tagjairól. Újbóli megnyomása hatására az örökölt tagok is megjelenítésre kerülnek.
Ctrl + Shift + O Az aktuálisan megnyitott típus import deklarációinak rendszerezése, a nem használt importok törlése, a hiányzó importok hozzáadása. Ha nem egyértelműen állapítható meg az importálni kívánt típus, akkor felugró ablakban lehetőséget kínál a találatok közül a megfelelő kiválasztására.
Ctrl + Shift + F Az aktuálisan megnyitott típus forráskódjának formázása, ha előtte kijelöltünk egy kódrészletet, akkor csak a kijelölt rész formázása.
Alt + Shift + S A Source menü megnyitása egy felugró ablakban, ahonnan lehetőségünk van a következő kódrészletek legenerálására:

  • metódusok felülírása
  • getter és setter metódusok generálása
  • delegáló metódusok generálása
  • hashCode() és equals() metódusok generálása
  • toString generálása
  • konstruktor generálása mezőkből
  • konstruktorok generálás ősosztályból
Ctrl + F11 Az aktuálisan megnyitott program futtatása.
F11 Az aktuálisan megnyitott program futtatása debug üzemmódban.
F6 Debug üzemmódban a jelölt sor utasításának végrehajtása és lépés a következő utasításra azonos szinten.
F5 Debug üzemmódban a jelölt sor utasításának végrehajtása úgy, hogy ha további metódushívást tartalmaz, akkor belépés egy hívási szinttel mélyebben.
F7 Debug üzemmódban a jelölt sor és az utána lévő sorok utasításainak végrehajtása és lépés az előző hívási szintre.
F8 Debug üzemmódban a program futtatása a következő breakpoint-ig.

Eclipse kódbányászat

Bár az új Eclipse Photon verziónak alapból nem része, legalábbis a mostani június release-nél, mégis említésre méltó bővítmény a JDT CodeMining. Segítségével az IntelliJ IDEA IDE-ből már talán általad is ismert feature-ök használhatók az Eclipse projekteknél. Ilyen például az osztályok és metódusok előtt megjelenő referenciaszámláló, vagy a metódusparaméterek nevének vagy típusának megjelenítése. A referenciaszámláló kattintható, aminek hatására egy keresést indít és annak eredményét jeleníti meg a Search view-ban, amely listázza a felderített hivatkozások forrását.

Nagyon hasznos plugin, javaslom kipróbálását! Telepítésének részleteit megtalálod a JDT CodeMining GitHub oldalán.

Eclipse gyorsbillentyűk hasznosságával vetekedő plugin

Összefoglaló

Hogy hatékonyan tudj programozni, érdemes megismerned az általad használt eszközök képességeit, így Java fejlesztés során az Eclipse IDE eszközkészletét, és hogy ezeket hogyan tudod a segítségedre hívni. A legjobb módja, ha begyakorlod a leggyakoribb gyorsbillentyűket, amiket a fentebbi táblázatban találsz.

Ha esetleg még most járnál programozói pályafutásod legelején, hadd ajánljam figyelmedbe a Java fejlesztői környezet beállítását képekben bemutató blog posztomat!

Ha bármi észrevételed van, esetleg javasolnál még további fontos billentyűparancsokat, amiket gyakran használsz, akkor kérlek, oszd meg véleményed egy hozzászólásban!

További jó kódolást kívánok neked! 🙂

Java 10 újdonságai – 1. rész

Máris itt a Java 10?

Java 10
Java 10

Nemrég történt, hogy a Java 9 újításai, a Project Jigsaw eredményeképp megvalósult modularizáció és társai napvilágot láttak, egészen pontosan 2017. szeptember 21-én. Előtte a Java 8 hivatalos megjelenése 2014. március 18-án volt, vagyis 3,5 év telt el a kettő között. A Java 10 pedig most egy hete 2018. március 20-án jelent meg, vagyis egy röpke fél év után.

Az Oracle – a Java gondnoka – új kiadási ütemezést tervezett meg, amelynek első alanya a Java 10. A terv, hogy félévente új főverzióját köszönthetjük a Java-nak, ami elősegíti mind a szoftverfejlesztő cégeknek, mind a programozóknak a könnyebb adaptálódást.

Mi a 12 újdonság a Java 10-ben?

Fél év alatt 12 JEP (Java Enhancement Proposal) készült el és került bele a 10-es verzióba:

  1. Local-Variable Type Inference
  2. Consolidate the JDK Forest into a Single Repository
  3. Garbage-Collector Interface
  4. Parallel Full GC for G1
  5. Application Class-Data Sharing
  6. Thread-Local Handshakes
  7. Remove the Native-Header Generation Tool (javah)
  8. Additional Unicode Language-Tag Extensions
  9. Heap Allocation on Alternative Memory Devices
  10. Experimental Java-Based JIT Compiler
  11. Root Certificates
  12. Time-Based Release Versioning

Az elkövetkezendő blog poszt sorozatban ezekről az új feature-ökről olvashatsz. Kezdjük az elején!

Helyi változó típuskikövetkeztetés

A beszédes Java

A Java mindig is híres volt arról, hogy kicsit bőbeszédű (sok a boilerplate kód). Az A&K Akadémia Java tanfolyamain is látom a tanulók arcán a csodálkozást, hogy miért kell mindent többször leírni. Gondolj csak egy egyszerű lista példányosításra:

List<String> list = new ArrayList<String>();

A Java 7 segített először nekünk, mert bevezette a diamond operátort, és a típusparamétereket legalább nem kellett kétszer leírni:

List<String> list = new ArrayList<>();

Java 10 - szóhoz se lehet jutni, akkora a fejlődés

És aztán 2 427 nappal később most a Java 10-ben:

var list = new ArrayList<String>();

Most már legalább a változó típusát nem kell kiírnunk, hanem használhatjuk a var szót.

Szándékosan nem írtam azt, hogy kulcsszót, mert a var a klasszikus értelemben nem egy új kulcsszó, mint mondjuk az int meg a super. A var nem kulcsszó, hanem foglalt típusnév. Épp ezért ha eddig használtad a var-t, mint helyi változónevet valamelyik projektedben, akkor ez nem fog most sem fordítási hibát okozni.

Ez teljesen rendben van:

var var = "Hello and welcome Java 10!";

Az, hogy a var foglalt típusnév, ez azt is jelenti, hogy csak olyan helyeken használható, ahol a Java fordító egy típusnevet vár.

Szép dolog ez a típuskikövetkeztetés, de hogy is működik pontosan?

Mi lesz a list változóm típusa, ha azt így definiálom?

var list = new ArrayList<String>();

A Java mérnökei a KISS elvet alkalmazták (Keep it simple, stupid) és a Java fordítót egy egyszerű, de hatásos kikövetkeztetési stratégiával áldtak meg:

Épp egy ArrayList-et példányosítasz a new operátorral? Akkor ez ArrayList típusú változó lesz.

Nyilván ez nem a leg high tech-ebb megoldás, de megteszi.

Lehetett volna kidolgozni egy eljárást, ami hogyha találkozik egy var változó definícióval, akkor onnantól kezdve átvizsgálja a kódot és megnézi, hogy ha az egy referencia típus, akkor a referencián keresztül az objektum milyen tagjait éri el a kód és ezek alapján a példányosított objektum típusából kiindulva megnézné, hogy az osztályhierarchián feleleve gyalogolva melyik az a típus, ami még eleget tesz az elemzésnek, és ezt a típust választhatná a változó típusának.

Ez elég komplex lett volna és egy olyan problémát okozott volna, amit nagyon nem szeretünk. Ez pedig az, hogy ha módosítjuk a kódunkat a 342. sorban, akkor ne romoljon el a 174.-ben.

Ezt a jelenséget action at a distance-nek hívjuk, és egy ellenminta (anti-pattern), vagyis kerülendő.

De az, hogy a Java-t fejlesztő mérnökök a típuskikövetkeztetésre az egyszerűbb módszert választották ez igazából teljesen érthető. A var csak helyi változókra működik. Nem használható se paraméter változónál, se mező szintű változónál. A helyi változók hatóköre pedig a legkisebb, itt a legelfogadhatóbb az, ha egy ArrayList-re nem List interfész típusú referenciával hivatkozunk. A helyi változók pusztán konkrét metódusok implementációs részletei.

Amit a var-ral nyer(het)ünk az pedig az olvashatóság. A programozói munka során sokkal többet olvassuk a kódot, mint írjuk. Ha az agyunknak nem kell ezt a plusz terhet elviselnie, hogy még a helyi változóknál is a referencia típusát feldolgozza, akkor több agyi kapacitásunk marad a többi, lényegesebb dologra, például hogy megértsük az üzleti logikát.

Ami még szintén jó hír, hogy a var igazából nem más, mint egy szintaktikai édesítőszer (syntactic sugar). A forráskód lefordítása során a var-os kifejezéseket a fordító helyettesíti a kikövetkeztetett típussal, tehát ebből

var list = new ArrayList<String>();

ez lesz:

ArrayList<String> list = new ArrayList<String>();

Így nem kell azon annyira aggódnunk, hogy egy új nyelvi feature használatával újabb, eddig nem ismert hibákat okozunk kódunkban.

Van var de nincs val. Miért?

Más programozási nyelvekben tipikusan a var és val is elérhető. Ez utóbbi a variable és a final szavak összevonásából keletkezett. A JEP fejlesztéseknél a közösség visszajelzéseit is mindig figyelembe veszik és a val esetén nem volt annyira szignifikáns a különbség, hogy bevezessék új nyelvi elemként. A döntést azzal is magyarázták, hogy még a helyi változók esetén a legkevésbé fontos a változó módosíthatatlansága, illetve a Java 8-ban bevezetett effectively final koncepció ezt már valamilyen szinten kezeli. Ha szeretnénk, akkor persze a var definíciónkat tehetjük final-lá ily módon:

final var list = new ArrayList<String>();

Mostantól minden helyi változót írjak var-ral?

Semmiképp. Mint minden nyelvi elem, ez is csak egy eszközt ad a kezünkbe, amivel tudunk jobb kódot írni. De akármennyire is szép és jó eszközök vannak a kezünkben, mindig megtalálhatjuk (és meg is találjuk) a módját annak, hogy lábon lőjük magunkat.

A var-nál sincs ez másként. Fontos, hogy mindig megfontold, hogy a kód, amit a segítségével írtál, az olvashatóbb lett-e. Gondolj bele miután megírtad a kódod, hogy mit szólna hozzá egy másik programozó, aki most került a projektedre és most találkozik ezzel a kódrészlettel először. Könnyíteni vagy nehezítené számára a kódolvasást?

To var or not to var?

A var, mint új nyelvi elem, egy olyan újdonság, amivel minden Java programozó találkozni fog. A megfelelő helyzetekben alkalmazva növelheti a kód olvashatóságát és ekkor érdemes használni. Csak helyi változóknál használható. De a var ellenére a Java továbbra is erősen típus nyelv maradt. Az út, amely az egyre könnyebben olvasható kód írásához vezet még hosszú, de a Java 7-ben a diamond operátor és most a Java 10-ben a var mérföldköveknek számítanak rajta. Így hát befejezésként azt mondhatom, hogy

Congratulations! You are being rescued! Please do not resist. 🙂

Java 9 újdonságai – 6. rész

Process API újítások

Előzmények

A Java korai verzióiban elég nehézkes volt új folyamatot indítani. Ehhez csak a Runtime.getRuntime().exec() metódus állt rendelkezésünkre. 2004-ben, a Java 5 megjelenésével ez megváltozott, innentől kezdve elérhetővé vált a ProcessBuilder API, amivel könnyebben lehetett létrehozni új folyamatokat. Nézzük meg, hogy mivel bővül a process API repertoárja a Java 9-ben!

process API szemléltetésére szolgáló ábra

ProcessHandle interfész

Az új ProcessHandle interfész új lehetőségeket nyit számunkra a natív folyamatok kezeléséhez. A Java 5 óta elérhető ProcessBuilder által előállított Process objektumoktól elkérhető azok ProcessHandle-je.

A ProcessHandle feladata, hogy azonosítson egy folyamatot és lehetővé tegye, hogy különféle műveleteket végezzünk el rajta. Példányok a következő statikus factory metódusok segítségével hozhatók létre:

Statikus factory metódus Létrehozott ProcessHandle objektum
current() Az aktuális folyamathoz tartozó ProcessHandle objektummal tér vissza.
of(long pid) Optional<ProcessHandle> objektummal tér vissza, ami a megadott natív folyamat azonosítóhoz tartozik.
children() Stream<ProcessHandle> objektummal tér vissza, ami az aktuális folyamathoz tartozó közvetlen gyerek folyamatokat tartalmazza.
descendants() Stream<ProcessHandle> objektummal tér vissza, ami az aktuális folyamathoz tartozó gyerek folyamatokat tartalmazza, rekurzívan azok gyerek folyamataival együtt.
parent() Optional<ProcessHandle> objektummal tér vissza, ami az aktuális folyamat szülő folyamatát tartalmazza.
allProcesses() Egy olyan Stream<ProcessHandle> objektummal tér vissza, ami az aktuális folyamat által látható össze folyamat ProcessHandle-jét tartalmazza.

További hasznos metódusok, amit a ProcessHandle interfész elérhetővé tesz:

ProcessHandle metódus Leírás
info() ProcessHandle.Info objektummal tér vissza, ami az adott folyamathoz tartozó információkat tartalmazza.
isAlive() boolean-nel tér vissza, ami azt jelzi, hogy az adott folyamat él-e még.
pid() A natív folyamat azonosítóval tér vissza, ami alapján az operációs rendszer számon tartja az adott folyamatot.
supportsNormalTermination() boolean-nel tér vissza, ami azt jelzi, hogy támogatja-e a normál leállítást az adott folyamat, vagy rákényszerítve, azonnal állítja le a folyamatot.
onExit() CompletableFuture<ProcessHandle> objektummal tér vissza, ami arra használható, hogy az adott folyamat befejeződésekor, szinkron vagy szinkron módon, elindítsunk egy tetszőleges utasítást.
destroy() boolean-nel tér vissza, ami azt mutatja meg, hogy az adott folyamat leállítási kérelme sikeresen fel lett-e dolgozva.

ProcessHandle.Info interfész

Egy folyamatra egy adott időpillanatban vonatkozó információit tartalmazza. Ezek az információk korlátozottak lehetnek az információkat igénylő folyamatra vonatkozó operációs rendszeri jogosultságok függvényében.

ProcessHandle.Info metódus Leírás
arguments() Az adott folyamat argumentumait tartalmazó String tömböt tartalmazó Optional objektummal tér vissza.
command() Az adott folyamathoz tartozó alkalmazás nevét tartalmazó Optional-lel tér vissza.
commandLine() Az adott folyamathoz tartozó parancssort tartalmazó Optional-lel tér vissza.
startInstant() Az adott folyamat indításának pillanatát tartalmazó Optional-lel tér vissza.
totalCpuDuration() Az adott folyamat által felhasznált CPU-időt tartalmazó Optional-lel tér vissza.
user() Az adott folyamat felhasználóját tartalmazó Optional-lel tér vissza.

Példa

public static void main(String[] args) throws InterruptedException, IOException {
    printProcessInfo("main", ProcessHandle.current());
    Process process = new ProcessBuilder("notepad.exe", "C:/teszt.txt").start();
    printProcessInfo("notepad", process.toHandle());
    process.waitFor();
    printProcessInfo("notepad", process.toHandle());
}

private static void printProcessInfo(String processDescription, ProcessHandle processHandle) {
    System.out.println("---------- Információk a(z) " + processDescription + " folyamatról ----------");
    System.out.printf("Folyamat azonosító (PID): %d%n", processHandle.pid());
    ProcessHandle.Info info = processHandle.info();
    System.out.printf("Parancs: %s%n", info.command().orElse(""));
    String[] arguments = info.arguments().orElse(new String[] {});
    System.out.println("Argumentumok:");
    for (String argument : arguments) {
        System.out.printf("   %s%n", argument);
    }
    System.out.printf("Parancssor: %s%n", info.commandLine().orElse(""));
    System.out.printf("Indítási idő: %s%n", info.startInstant().orElse(Instant.now()).toString());
    System.out.printf("Futási idő: %sms%n", info.totalCpuDuration().orElse(Duration.ofMillis(0)).toMillis());
    System.out.printf("Felhasználó: %s%n", info.user().orElse(""));
    System.out.println();
}

Összefoglalás

A Java 9 process API újításai segítségével szebb kódot írhatunk. A korábbi verzióban bevezetett ProcessBuilder is jelentős lépés volt a helyes irányba, de az új lehetőségek, amiket az új interfészek, illetve az azokat megvalósító osztályok biztosítanak, fontos új eszközöket adnak a programozók kezébe, amikkel könnyebben kezelhetővé váltak a natív folyamatok.

Ha a többi Java 9-es újítás is érdekel, akkor böngészd bátran a többi blog posztot is ebben a témában!

A hivatalos javadoc-ban minden további részletet megtalálsz:

ProcessBuilder, Process, ProcessHandle, ProcessHandle.Info, CompletableFuture.

Minden, amit mindig is tudni akartál a programozásról (de féltél megkérdezni)

 

Gyakori kérdés, hogy programozó karrier diploma nélkül is építhető-e és milyen esélyekkel indul neki valaki a programozásnak, ha nincs szakirányú végzettsége?

A programozói karrierről, a diploma szükségességéről, önfejlesztésről és szórakozásról beszélgettünk exkluzív interjú keretében Almási Zsolttal, partnercégünknél, a P92-nél dolgozó fejlesztővel.

 

Egy finom tejeskávé társaságában az A&K Akadémia egyik tantermében

 

Mesélj kicsit magadról, hogyan lettél programozó?

Neumann János Számítástechnikai Szakgimnáziumba jártam. Akkor úgy gondoltam, hogy ebből 5 év elég is volt, nem akarok programozó lenni. Elmentem két egyetemre, de egyiket sem fejeztem be, fél évig jártam mindkettőre. Több bulin voltam, mint órán, eléggé elhanyagoltam. 🙂 Aztán elvégeztem egy OKJ-t. Ezt csak azért csináltam meg, hogy legyen papírom róla, hogy informatikában jártas vagyok. Véletlenül találkoztam a mostani főnökömmel és beszélgetés közben kiderült, hogy állást keresek, és tudta, hogy értek a programozáshoz, és bár diplomám nincs, behívtak interjúra és végül fel is vettek. Gyorsan pörögtek az események. Elvégeztem egy 6 hónapos belső képzést és aláírtam egy két éves szerződést. Eddig 3 projektben vettem részt. Körülbelül májusban lesz két éve, hogy itt vagyok.

Mi volt a legnehezebb az elején?

Egy olyan keretrendszerrel találkoztam, amit egyáltalán nem ismertem, ráadásul a keretrendszer maga a kód fele, tehát ha nem ismerem, akkor nem ismerem magát a kódot sem. Azt volt nehéz megtanulni, hogy hogyan kell használni. Ráadásul a kód sem volt egyszerű, egy metódus többezer sorral és benne nagy „if”-ekkel.

Hazaviszed a munkát?

Csak ha van kedvem, és ha érdekel annyira, hogy foglalkozzak vele otthon is. Többet szoktam azon gondolkodni, hogy hogyan lehet egy feladatot röviden megoldani, mint hogy nekiálljak a hosszú útnak. Az idő egy része azzal telik, hogy gondolkodom a rövidebb, könnyebb megoldáson. Nem fogok órákat gépelni, hogy ha meg tudom csinálni sokkal egyszerűbben is. A Java-ban az a nehéz, hogy rengeteget kell benne kódolni. Semmi kedve az embernek órákig csinálni. Keresni kell a rövidebb utakat, mert biztosan meg lehet csinálni egyszerűbben is. 🙂

Ha újrakezdhetnéd, akkor mit tennél másképp?

Ha újrakezdhetném, akkor azonnal, ahogy elvégeztem a középiskolát, elmentem volna egy ilyen helyre dolgozni, mint a P92. Ennek van értelme. Esetleg mellette megcsináltam volna az egyetemet, de nem biztos. Szerintem az egyetemnek önmagában semmi értelme nincs, úgy pedig pláne nem, hogy arra 3,5 – 5 évet tisztán rászánjak. Felesleges ebben a szakmában. Itt sokkal többet számít a gyakorlati tapasztalat.

Szerinted mit jelent, ha valaki diplomás?

Az egyetem önmagában annyit jelent, hogy van elég kitartásod elvégezni, tudsz tanulni, képes vagy magadra erőltetni a folyamatos tanulást, különben nem tudod befejezni az egyetemet. Ebben jó. Illetve bizonyos szinteken már kell a matek is, és ott tanítanak ilyet. De ez is ritka, és csak bizonyos pozíciókban van rá szükség. Külön cégek foglalkoznak ilyesmivel. Üzleti szoftverekhez nem kell matek, itt már meg van írva az a rész, csak használni kell tudni.

Gondolkodtál azon, hogy most utólag elvégzed az egyetemet?

Hááát… legfeljebb csak a papírért. Talán. De minden álláshirdetésnél az van odaírva, hogy BSc, MSc vagy azzal egyenértékű tapasztalat az adott területen. Az meg megvan. 🙂

Érezted valaha hátrányos helyzetben magad azért, mert nincs felsőfokú végzettséged?

Nem. Még nem fordult elő, hogy a papír hiányzott volna. Az ott megszerezhető tudás sem nagyon hiányzott. A matek egyszer jól jött volna, mert volt egy könyv a funkcionális programozásról, amit el akartam olvasni, de nagyon belemerült a részletekbe, és nem minden részt értettem pontosan.

Mit tanultál meg a felvételi utáni belső képzésen?

A fél éves belső képzésen a sebességet növeltük, mert ezen a munkahelyen azért kell tartani egy bizonyos tempót. Ezen kívül megtanították, hogy hogyan tudok utána nézni dolgoknak, hogyan tudom fejleszteni saját magam. Meg kellett szokni, hogy mindig újabb és újabb dolgoknak kell utána nézni.

Milyen könyveket ajánlanál kezdőknek?

The pragmatic programmert, a Clean Codert. Egyik sem kódspecifikus, inkább ilyen soft skilles. Ezenkívül a Java Core-t és a Clean codingot is mindenképp ajánlom. Ezeket angolul olvastam, magyarul nincsenek olyan jó könyvek, és ha le is fordítják magyarra, sosem fogod úgy látni azokat a kifejezéseket. A gyakorlatban úgy is minden angolul van. Ha megtanulod magyarul, és rákeresel, akkor nem találsz róla semmit, és fejben átfordítani utána elég nehéz. Jobb eredetiben olvasni.

Szerinted mennyire fontos a szakmai életben az angol? Te hogyan tanultál meg angolul?

Régebben heti 14 órában tanultam az angolt, és most angolul olvasok, tanulok, dolgozom. Az, hogy angolul kell tudni, az nem lehet kérdés. A könyveket meg kell tudni érteni, hogy tudd magad otthon fejleszteni. Informatikai szaknyelvből nem középszinten vagyok, sokkal magasabban. Lehet, hogy középszinten vagyok angolul mondjuk főzésből, mert zöldséget csak kb. tíz darabot tudok mondani. 🙂 De szaknyelvből kellenek a szavak. Sok mindent nem is tudok magyarul, csak angolul.

Most lehet, hogy németül kellene megtanulnom, mert gondolkodtam azon is, hogy kimennék Bécsbe dolgozni. Az is csak 3 óra vonattal, nem távolság, bár ha elkészül a bullet train akkor csak 13 perc lesz. 🙂 Hamarabb kiérnék vele Bécsbe, mint innen a Nyugatiba. Megérné kimenni dolgozni, mert ott még magasabbak a fizetések. A normál fizetés is 60 ezer euró évente. Még ha a harmadik legdrágább európai város, akkor is megéri.

Oda nem elég az angol nyelvtudás?

Az angol alapelvárás, de azért azt minden német álláshirdetésben írják, hogy egy kommunikációs szinten illik tudni németül is. Lehet, hogy a munkatársak egy része nem beszél angolul? Nem tudom.

Visszatérve a korábbi gondolathoz: ha jól értem, te most saját magadat fejleszted?

Igen. Olvasni és tanulni egyszerűen muszáj. Lehet egyedül is tanulni, de persze az a legjobb, ha van egy mentora az embernek, aki segít. Az nagy előny. Csak nehéz jó mentort találni. Nekem szerencsém volt itt a cégnél, mert mellettem volt István, aki csak úgy dobálta nekem a linkeket és könyvcímeket, hogy mi mindent olvassak el, tanuljak meg. Nagyon sokat segített a kifejezésekkel, hogy mire érdemes rákeresni. Ez tök jó volt, mert igazából rám volt bízva, hogy mennyit tanulok. Nagyon sokat segített benne.

Mit tanácsolnál egy teljesen kezdőnek, aki most kezd el először programozni?

Keressen rá a népszerű programnyelvekre, és válasszon egy szimpatikusat közülük! Szerintem a Java nagyon jó. De ahhoz, hogy önállóan tovább tudja saját magát fejleszteni, ahhoz először valahogy el kell érnie egy bizonyos szintet. Hiszen, ha nem tudja, hogy mit keressen, hogyan működik ez az egész, akkor esélytelen. A kezdeteknél mindenképp jó egy mentor. Ha nem tudja, hogy miben kell fejlődnie, akkor az nagy pech, ha egyedül van, és nincs kitől kérdezni, tanácsot kérni.

Milyen a jó mentor, és miért?

Legfőképp türelmes. Programozni elég nehéz dolog. Nem olyan egyszerű, mint ezt olyan sokan mondják, hogy ott van, elég elolvasni és kész. Sok helyen ez jön le a netről, de ez nem igaz. Próbálják könnyűnek mutatni, pedig nem egy könnyű dolog, szerintem. A valódi munka nem arról szól, hogy írsz valami újat. Bele kell nyúlni a meglevőbe! Senki a bolygón nem kap olyan feladatot, hogy magától írjon, tisztán egy programot, ezt megmondom neked itt a széken ülve. A valóság arról szól, hogy egy már meglevő rendszerbe kell belenyúlni felelősséggel, és az nem egyszerű. És nap, mint nap ezt kell csinálni.

 

 Szerinted mire számítsanak a kezdők a tanfolyamunk alatt?

Lesznek nehéz pontok, amin át kell majd esniük ebben biztos vagyok. Mindenkinek lesz olyan, hogy nem érti meg, amit tanul, és ülnie kell rajta 3-4 napot, és még azután is szembe kell néznie azzal, hogy nem érti meg. Idő kell neki, mire az ember rááll. Még a mai napig is simán van olyan, hogy valamit nem tudok megcsinálni és akkor egy héttel később egyszer csak katt, és beugrik. És akkor megvan! Tehát türelem kell, mindenképp! Vannak dolgok, amikhez idő kell, nem megy másképp. Lesz olyan, hogy Andris elmondja egyféleképp, nem értik meg, másképp, harmadik módon és mondhatná tizenötféleképpen, akkor sem megy. Van ilyen. Az a helyzet már nem a magyarázaton múlik. Érnie kell a gondolatnak és majd hetekkel később meg lesz az a megoldás. Erre fel kell készülni. Mindenki másképp működik.

Az elmúlt évek során volt, hogy elvesztetted a motivációdat, és beleuntál?

Persze, unalmas részek is vannak. De mit lehet ilyenkor csinálni? Beleharapok az ajkamba és várok. 🙂 Meghallgatom, megnézem, és ennyi. Ha szükségem van rá, újra és újra rákeresek, utána nézek, és előbb-utóbb megmarad. Amikor gondolkodok rajta, akkor általában gyorsan megtanulom, de bemagolni semmit nem magolok be. Felesleges. A leghatékonyabb tanulás a gyakorlás. Én sokszor elég lusta vagyok ehhez, de én is látom, hogy kell. Be kellene áldozni 1-2 órát gyakorlásra.

Most, hogy ebben dolgozol, most is folyamatosan tanulsz?

A jó pap is holtig tanul, nekünk is muszáj. Szinte havonta változik az egész, tartani kell a lépést. Muszáj olvasni, tanulni. Nem szabad abbahagyni, mert lehagy a szakma. Be kell áldozni valamit, hogy legyen rá idő. Vagy kevesebbet dolgozok konkrétan, és utána járok dolgoknak, vagy ha munka mellett nem fér bele, akkor otthon kell utána járnom. A P92-nél is van lehetőség tanulni. Angolra is lehet járni, Clean code-ra, Refactoringra, stb., heti több órában.

Próbáltad már tanítani azt, amit megtanultál?

Nem, még nem. Ha valakinek van valami problémája, elakadása, akkor szívesen segítek, persze. De tanítani az más dolog.

Milyen álmaid vannak a jövőre nézve?

Több fizetés, esetleg külföld. Volt egy lehetőség itt a P92-nél is, hogy ki lehet menni Amerikába, meg is pályáztam, de sajnos nem engem választottak. Külföldön a legtöbb helyen angol kell, úgyhogy ez nem lesz gond a jövőben sem.

Jelenleg meg vagy elégedve a fizetéseddel?

Meg vagyok elégedve, igen. Többet keresek, mint más ismerőseim, nagy átlagban. Persze vannak elképzeléseim a jövőre nézve, ha lejár a két éves szerződésem, de ez így rendben volt eddig.

Nem aggódsz, hogy mi lesz utána?

Nem. Tele van a net álláshirdetésekkel. Nem szoktam sokat aggódni, nincs miért. Eddig sem kellett sokat interjúra járnom. Anno összesen 2 helyre adtam be a CV-met, a P92 volt az egyik. Szóval kb. azonnal sikerült. Szinte azonnal fel lettem véve. Válogatós vagyok és szerencsére lehet is válogatni. Régebben azért nem mertem sok helyre elküldeni a CV-met, mert tudtam, hogy nem felelek meg, de ez most már nincs így. Most már megfelelek, van önbizalmam.

Van hobbid?

Most tanulok japánul, király. 🙂 Nem a megtérülés miatt, csak egy drága hobbi. De amúgy is tele vagyok olyan hobbikkal, amik időt és pénzt vesznek el. 🙂

A PC-s játékoknak is van haszna, csak nehéz bemutatni őket, de az életben kijön, hogy ha jó vagy ezekben a játékokban, akkor az életben is bizonyos dolgokhoz valahogy jobban értesz. Csak erre nem lehet könnyen rámutatni, hogy ez és ez az a pont, amiben jobb vagy. Játszva tanul velük az ember. Szerintem jók. Ha tudsz párhuzamot vonni a játék és egy másik dolog között, akkor nagyon hasznos. Rendszerezni is megtanulsz, nyelvet gyakorolsz, reakcióidőd is nőhet, persze játéktól függ.

Ezeken kívül sokat olvasok. Egy évben elolvasok vagy húsz ponyvát (pl. a kedvenc kiadóm az Agave), és kb. három komolyabb szakmai könyvet. Egy 400 oldalas ponyvát kb. egy nap alatt elolvasok. Leülök, vagy az utcán olvasok, utazás közben, sőt, séta közben is tudok olvasni.

Hogy állsz a soft skillekkel?

El tudom adni magam. Ha már beszélgetnem kell valakivel, akkor már előnyben vagyok. Nekem ez nem gond. Komoly konfliktusom sem volt munkahelyen senkivel. Kreatív viták vannak, vagy van, hogy hangosabbak vagyunk, de semmi komoly.

A soft skillek fejlesztéséhez van valami jó tanácsod a most induló csoportnak?

Elküldeném őket egy buliba, hogy szedjenek fel egy nőt vagy férfit! Oda pont ugyan ezek a készségek kellenek. 🙂

Köszönjük szépen a beszélgetést! 🙂

Java 9 újdonságai – 5. rész

Collection factory metódusok

A Collection factory metódusok olyan új statikus metódusok, amik segítségével egyszerűbben, tömörebben és biztonságosabban inicializálhatunk collection adatstruktúrákat.

Előzmények

A Collections Framework létezése óta – vagyis már a JDK 1.2 óta (1998) – sok fejlesztőt elgondolkodtatott, hogyan lehet egy ArrayList-et egy sorban inicializálni. Bár a Java nyelvi támogatást nyújt a String literálok esetén, nem ez a helyzet a Collections Framework-nél.

Szimpla kóderek

Bár Collections literálok nincsenek a Java-ban, ahogy más referenciatípusoknál is, itt is jogos az igény az inicializálásra. Erre a legnyilvánvalóbb – mondhatni triviális – megoldás a következő:

List<String> shoppingList = new ArrayList<>();
shoppingList.add("1 kg kenyér");
shoppingList.add("1 l tej");
shoppingList.add("10 db tojás");

Bár ez a megoldás helyes, mégis nagyon bőbeszédű. Tudsz ennél jobbat?

Leleményes kóderek

A fenti megoldás kicsit túl bőbeszédű, indokolatlan mennyiségű kódot kell leírni ahhoz, hogy egyszerűen alapértékre állítsunk be egy listát. Létezik egyszerűbb megoldás:

List<String> shoppingList = Arrays.asList("1 kg kenyér", "1 l tej", "10 db tojás");
shoppingList.add("1 kg liszt"); // se hozzáadni
shoppingList.remove(0);         // se törölni nem tudunk

Így már sikerült egy sorossá tenni ezt az inicializációt, de érdemes megjegyezni, hogy az előző megoldással ez nem ekvivalens, van egy jelentős különbség az így alapértékre állított változó használatában. Ez a különbség névlegesen az, hogy az így példányosított lista nem méretezhető át, vagyis nem adhatunk hozzá újabb elemet és nem is törölhetünk belőle. Ha makacsak vagyunk és mégis megpróbálnánk, akkor futásidőben a virtuális gép egy UnsupportedOperationException-nel lep meg minket.

Figyelmesen elolvasva az Arrays.asList(T… a) metódus javadoc-ját fény derül ennek okára. Itt elmagyarázzák nekünk, hogy ez a metódus a könnyebb átjárhatóságot hivatott biztosítani a régebbi tömb alapú API-k és az újabb Collection alapú API-k között. A visszaadott lista objektumon végzett változtatások „átíródnak” a lista alapját szolgáló tömbbe. Érthető tehát, hogy mivel a tömbök is létrehozásuk után fix méretűek, így az Arrays.asList(T… a) metódus által visszaadott lista is fix méretű lesz.

Még leleményesebb kóderek

Egy egyszerű csavarral az előző megoldásból kerekíthetünk egy módosítható listás megoldást, ami első bőbeszédű változattal egyenértékű funkcionalitásban:

List<String> shoppingList = new ArrayList<>(Arrays.asList("1 kg kenyér", "1 l tej", "10 db tojás"));
shoppingList.add("1 kg liszt"); // tudunk hozzáadni
shoppingList.remove(0);         // tudunk törölni

Ez nagyszerű, de kezdünk megint kicsit túl bőbeszédűek lenni. Túl sok billentyűt kell leütni ahhoz, hogy inicializáljuk a listánkat. Vajon van jobb megoldás?

Túl leleményes kóderek

Ha alaposan ismered a Java nyelv alapvető építőköveit, akkor bizonyára hallottál a példány inicializátorokról (instance initializer). Ha ezt vegyítjük egy kis névtelen belső osztállyal, akkor eredményül egy extra trükkös, első ránézésre újszerű szintaxist kapunk:

List<String> shoppingList = new ArrayList<String>() {{add("1 kg kenyér"); add("1 l tej"); add("10 db tojás");}};
shoppingList.add("1 kg liszt");
shoppingList.remove(0);

Elértük, amit szerettünk volna, egy sorban inicializáltuk a listánkat. Még mindig van néhány feleslegesnek tűnő karakter és elég sok szintaktikai elem. Ezt a megoldást szokták dupla kapcsoszárójeles inicializálásnak (double brace initialization) hívni. Bár valóban egysoros lett ez a művelet, számos dolog történik a háttérben. Vegyük sorra:

  1. Először is létrejön az ArrayList osztályból leszármaztatott névtelen osztály.
  2. Ennek az új névtelen osztálynak létrejön egy példánya.
  3. Mivel a belső kapcsoszárójel az egy példány inicializátor blokk, ezért ez a kódblokk lefut a 2. lépésben történő példányosodás során.
  4. Az utasításblokk az add() példány metódust meghívja egymás után különböző paraméterekkel, ami mivel nincs override-olva, ezért a szülő osztályból (ArrayList) megörökölt publikus add() metódust hívja meg, ami hozzáadja a paraméterként kapott elemeket a listához.
  5. A most létrejött névtelen osztály példányának referenciáját hozzárendeli a shoppingList változóhoz.

Elég sok dolog végbemegy egyetlen sorban. Ráadásul a névtelen belső osztály miatt a Java fordító külön class fájlokat gyárt a fájlrendszeren. Ha sokat használjuk ezt a trükköt, akkor a túl sok plusz class fájl miatt a programunk lelassulásának lehetünk tanúi. Az objektumorientáltság egyik alapműveletét a leszármaztatást itt nem arra használjuk, amire hivatott, mondhatni visszaélünk vele.

Tudván ezeket kijelenthetjük, hogy bár érdekes módszer ez, mégis kerülendő, anti-patternnek tekintendő.

Java 9 Collection factories to the rescue!

A Collection factory metódusok, amiket a Java 9-ben vezetnek be, elegáns megoldást nyújtanak:

List<String> list = List.of("1 kg kenyér", "1 l tej", "10 db tojás");
Set<String> set = Set.of("1 kg kenyér", "1 l tej", "10 db tojás");

Ez már tényleg sokkal jobb, mint az eddigi megoldások. Nincs túl sok szintaktikai elem és nem is kell sokat gépelnünk. Mégis van egy kis probléma, ami persze a felhasználók szempontjából igazából lényegtelen, de jó tudni róla.

Ennek az új of metódusnak pontosan mennyi paramétert is tudunk átadni? A példánkban épp 3-at adunk át, de mi van, ha valakinek kevesebb vagy több kell?

A Java 5-ös verziójában bevezették a változó hosszúságú argumentumokat (varargs). Ez lényegében egy szintaktikai édesítőszer, ami lehetővé teszi, hogy egy metódusnak ne tömböt kelljen átadnunk, hanem vesszővel felsorolhassuk az argumentumokat. A következő két metódus egyenértékű:

void method() {
    String[] shoppingArray = { "1 kg kenyér", "1 l tej", "10 db tojás" };
    methodWithArray(shoppingArray);
    methodWithVarargs(shoppingArray);
    methodWithVarargs("1 kg kenyér", "1 l tej", "10 db tojás");
}

void methodWithArray(String[] shoppingArray) {
}

void methodWithVarargs(String... shoppingArray) {
}

A változó hosszúságú argumentumoknak persze vannak megkötései, például hogy az ilyen paraméter csak a paraméterlista végén szerepelhet, illetve egy metódusnak csak egy ilyen paramétere lehet. Amikor változó hosszúságú argumentumokat váró metódust hívunk, akkor a háttérben létrejön egy tömb, ami hordozza az elemeinket. Ez persze némi teljesítménybeli visszaesést okoz, főleg ha sokszor futtatunk ilyen kódot, például ciklusban.

Hogy ezt elkerüljék, a Java fejlesztői az új of metódusnak nem csak egy változatát készítették el, hanem másik 10 overloadolt változatát is, ahol az első egy paramétert vár, a második kettőt, a harmadik hármat, és így tovább. Így amikor egy, kettő, három … tíz argumentummal hívjuk, akkor a megfelelő overloadolt változat fut le, és így elkerüljük a varargs esetén fellépő overheadet. Cserébe viszont „teleszemetelték” a Java core kódját egy csomó redundánsnak tekinthető résszel, így ezentúl több kódot kell majd karbantartaniuk az elkövetkezendő verziókban. A Java-t fejlesztő szakemberek ezt a megközelítést tartották célszerűnek.

De nem csak a List és Set kapott új factory metódusokat, hanem a Map is:

Map<String, Integer> shoppingMap = Map.of("kenyér", 1, "tej", 1, "tojás", 10);

Tíz argumentumig itt is overloadolt metódusokat használhatunk a kulcs-érték párok felsorolásához, de efelett szintén változó hosszúságú argumentummal rendelkező factory metódust tudunk hívni, amit Map.Entry<K, V> objektumokba kell csomagolnunk, amihez kapunk cserébe statikus entry() metódust:

Map<String, Integer> shoppingMap = Map.ofEntries(entry("kenyér", 1), entry("tej", 1), entry("tojás", 10));

Az új statikus Collection factory metódusok fontos tudnivalói

Nem csak a bőbeszédűség csökkentése volt a cél, bár kétségtelenül nagy előny, hogy tömörebben meg tudjuk fogalmazni a kódunkat és könnyebb is átlátni. A programozói hibák is csökkenthetők néhány általános érvényű szabály bevezetésével, amit az elmúlt években már eszközöltek is, és amire most is ügyeltek.

Megfigyelték, hogy a programhibák jelentős százaléka a null értékek helytelen használatából ered. Épp ezért a collection-ös adatstruktúrákban nem használhatjuk a null-t elemként. Ezt ezek az új statikus factory metódusok se engedélyezik.

A másik hibacsökkentési lehetőség az immutabilitás (módosíthatatlanság) bevezetése. Ezek a Collection factory metódusok módosíthatatlan adatstruktúrákat produkálnak. Ez azért jó, mert sok hiba abból fakad, hogy egy módosítható adatstruktúrát a program hívási láncolata mentén végigpasszolva valahol akaratlanul módosítjuk.

Egy másik nagy előnyt azt a Map.ofEntries() megvalósítása nyújtja, ugyanis itt nem tudjuk elrontani a kulcs-érték párok párosítását, ami csak futásidőben derülne ki, mert már fordítási időben hibát kapunk és hamar kijavíthatjuk azt.

A HashSet-ek és HashMap-ek elemei mindig is látszólag véletlenszerű iterálási sorrendet eredményeztek, de volt egy determinisztikus voltuk, hogy ugyanazt a sorrendet követték minden programfutás esetén. Az új immutábilis collection-ök viszont minden futásnál más és más sorrendben iterálnak végig az elemeiken, így ha hibásan egy kódrészlet a rendezetlen elemek valamilyen sorrendjére támaszkodott, akkor most az ilyen hibákra hamar fény fog derülni (ezt hívjuk fail fast viselkedésnek).

Konklúzió

Összességében az új Collection factory metódusok egy nagyszerű új lehetőséget biztosítanak a programozók számára, amivel tömören tudják a kódjukat megfogalmazni. Érdemes tisztában lenni a részleteikkel, mint például, hogy immutábilis példányokkal térnek vissza, tiltják a null használatát és az iterálási sorrendjük is változó lehet.
A JEP 269-ben olvashatod el angolul a motivációt a megvalósítás mögött.
Ha érdekel a Java 9 többi újdonsága is, akkor azokat a Java 9-es blog posztjainkban olvashatod.