Späť na blog
Tipy a triky

Lambda výrazy v Jave - časť III.

Skillmea tím
03.10.2019
6 minút čítania

Lambda a vnútorné anonymné triedy

Veľmi sa nám žiada povedať, že lambda výrazy sú len skratky ako napísať vnútorné anonymné triedy. Ale pamätaj si, nie je to tak. Vyzerá to podobne ale lambda nie je implementácia rozhrania. Lambda je sama osobe nezávislá iná vec.
Pozrime sa na príklad. Namiesto toho aby sme použili implementačnú triedu nášho rozhrania IHelloWord, vytvoríme si vnútornú anonymnú triedu.

IHelloWord helloWord3 = new IHelloWord() {
    @Override
    public void sayHello() {
        System.out.println("HelloWord impls inner anonymous class");
    }
};

Všetky 3 možnosti, ktoré majú ako návratovú hodnotu rozhranie IHelloWord môžeme podsunúť do metody printHelloWord(IHelloWord helloWord).

helloWord.printHelloWord(helloWord1);
helloWord.printHelloWord(helloWord2);
helloWord.printHelloWord(helloWord3);

Ako to, ako to?


Ako java vie, aký má použiť typ pre lamba výraz? Aby sme tomu porozumeli, vytvoríme si novú triedu, kde budeme pracovať s lambda výrazom.
Vytvorme si rozhranie, ktoré bude mať jednu metódu, ktorá bude vracať int a na vstupe bude tiež int.

interface Nasob{
    int nasob(int a);
}
Ako by vyerala implementácia tohto rozhrania?
class NasobPiatimi implements Nasob{
    public int nasob(int a){
        return a*5;
    }
}

Teraz si navrhnime lambda výraz, ktorý zodpovedá danej metóde. Nepotrebujeme návratovú hodnotu int, lebo java vie na ňu prísť sama a nepotrebujeme ani názov metódy a ani modifikátor prístupu public. Náš lambda výraz bude vyzerať takto:

(int a) -> a*5;

Teraz použime tento lambda výraz:

public static void main(String[] args) {
    Nasob nasobPiatimi = (int a) -> a*5;
    System.out.println(nasobPiatimi.nasob(10));
}

Na výstupe bude 50. V tomto príklade sa lambda tvári ako instancia rozhrania Nasob. V predchádzajúcich príkladoch, keď sme používali HelloWord sme takúto premennú vkladali ako parameter metódy printHelloWord (HelloWord3 v IDEi). Namiesto toho sme mohli túto lambdu vložiť priamo do metódy.

helloWord.printHelloWord(() -> System.out.println("HelloWord impls lambda"););

Java kompilátor vezme tento lambda výraz a pozrie sa kam ide. Ide to metódy printHelloWord a pozrie sa, čo akceptuje na vstupe. Akceptuje rozhranie HelloWord. Ak lambda sedí s požiadavkou, že dané rozhranie obsahuje len jednu metódu a tá vracia void a na vstupe nemá žiaden parameter, tak java povie, že daná lambda je typu HelloWord. Toto sa volá Type inference. Java si sama zistí typ. Teraz, keď vieš ako java dokáže zistiť typy, vrátime sa ku príkladu, ktorý sme začali písať v tejto kapitole. V našom príklade vieme ešte viacej skrátiť zápis nášho lambda výrazu.

Nasob nasobPiatimi = (int a) -> a*5;
System.out.println(nasobPiatimi.nasob(10));

Keďže naša lamba ide do metódy rozhrania, ktorú poznáme

interface Nasob{
    int nasob(int a);
}
Tak vieme presne povedať aký typ má vstupný paramter metódy. Je to int.
interface Nasob{
    int nasob(int a);
}

Keď to vieme, tak nemusíme pri písaní lambda výrazu znovu špecifikovať typ vstupného parametru.

Nasob nasobPiatimi = (a) -> a*5;

A keďže máme len jeden parameter, nemusíme písať ani zátvorky.

Nasob nasobPiatimi = a -> a*5;

Už nebudeme nič mazať, lebo by nám už nič neostalo 😃

Teraz môžeme napísať metódu, ktorá bude na vstupe očakávať rozhranie Nasob a keď ju použijeme, tak do nej vložíme na vstup náš lambda výraz.

public static void printNasob(Nasob nasob){
    System.out.println(nasob.nasob(10));
}
public static void main(String[] args) {
    printNasob(a -> a*5);
}

V jave mohli kľudne vytvoriť nový typ pre tieto lambda výrazy. Ale nespravili to a jedným z dôvodov je aj spätná kompatibilita so starším kódom. Ako už vieme, tak lambda výrazy môžeme použiť všade tam, kde máme vyhovujúce rozhranie.

Vo vnútorných anonymných triedach, v metódach kde je na vstupe interface a podobne. Príklad:

HelloWord helloWord3 = new HelloWord() {
    @Override
    public void sayHello() {
        System.out.println("HelloWord impls inner anonymous class");
    }
};
HelloWord helloWord3 = () -> System.out.println("HelloWord impls inner anonymous class");

Pri tomto musíme pamätať, aby rozhrania boli jedno metódové alebo aby ostatné metódy rozhrania boli default. A dané metódy v rozhraniach, aby sa zhodovali s lambda výrazom. Takéto rozhranie s jednou abstraktnou metódou (metóda, ktorá poskytuje opis nie implemntáciu) sa nazýva Functional interface.
Predstav si, že používaš rozhranie, ktorý má len jednu metódu a používaš ho pre lambda výrazy. Teraz by niekto cudzí prišiel a do tohto rozhrania by pridal ďalšiu abstraktnú metódu, presnejšie jej opis bez implementácie. Takéto rozhranie by už viac nebolo functional interface a preto by sa nemohlo použiť pre lambda výraz a nastala by chyba – napriek tomu, že rozhranie by bolo v poriadku. Treba na to myslieť a ak chceme niečo pridať do functional interfac, tak len ako default metódy.
Aby sme upozornili kohokoľvek, kto by chcel niečo pridať do nášho rozhrania, tak máme možnosť pridať anotáciu @FunctionalInterface. K anotáciam sa ešte dostaneme, tak sa nebojte. Teraz je dôležité vedieť, že je to pomôcka – táto pomôcka nám spraví to, že hneď ako napíšeme ďalšiu metódu do nášho rozhrania, tak nastane chyba. Danú anotáciu nemusíme písať, ale je to super.

@FunctionalInterface
public interface HelloWord {
    void sayHello();
}

Príklady na vyskúšanie:

  1. vytvor si zoznam miest
  2. zotrieď zoznam
  3. napíš metódu, ktorá vypíše všetko zo zoznamu miest
  4. urob si metódu, ktorá vypíše len tie mestá, ktoré sa skladajú z jedného slova nepoužívaj pri tom lambda výrazy

Pokračovanie nabudúce 👋

Články a online kurzy o Jave pre teba pripravuje Jaro Beňo.

Skillmea tím

Sme jednotka v online vzdelávaní na Slovensku.

Na našom webe nájdeš viac ako 260 rôznych videokurzov z oblastí ako programovanie, tvorba hier, testovanie softwaru, grafika, UX dizajn, online marketing, MS Office a pod. Vyber si kurz, ktorý ťa posunie vpred ⏩

Mohlo by ťa zaujímať

Tipy a triky
23.08.2019
Skillmea tím

Lambda výrazy v Jave - časť II.

Typy lambda výrazovTento článok je pokračovaním prvej časti tutoriálu o Lambda výrazoch. Vráťme sa k nášmu príkladu na začiatku, kde sme do metódy vložili implementáciu rozhrania a zavolali sme metódu. Toto si teraz skúsime spolu prerobiť, tak aby sme použili lamba výraz.  HelloWord2. Prepíšme metódu implementácie na lambda výraz. public void sayHello() { System.out.println("HelloWord impls"); } () -> System.out.println("HelloWord impls"); Znovu tá istá otázka, aký typ má lamba výraz ak ho chcem priradiť do premennej? lambdaFunkcia = () -> System.out.println("HelloWord impls");Počkať! V Jave predsa máme možnosť ako deklarovať, vymenúvať metódy a tú použijeme. Tou možnosťou je použiť rozhrania. 2. vytvorenie rozhrania s jednou deklaráciou metódy 4. vytvorenie metódy, ktorá odpovedá našemu lambda výrazu 1. v našom prípade nemá žiadne argumenty a návratová hodnota je void Ak by sme vytvorili rozhranie, kde budeme uvádzať viacero metód, tak to bude chyba. Jedine ak budú default – teda implementované. Viď sekciu o rozhraniach. ILambda lambdaFunkcia = () -> System.out.println("HelloWord impls"); … public interface ILambda { void metoda(); // void metoda2(String s); // bude error }Použime lambda výraz s bezpečným delením. Napíšme si k tomu interface a premennú: bezpecneDelenieFunkcia = (int a, int b) -> { if(b==0) { return 0 ; } return a/b; }; public static void main(String[] args) { Hocijako hocijako = (int a, int b) -> { if(b==0){ return 0; } return a/b; }; double d = hocijako.hocico(10, 2); System.out.println(d); } interface Hocijako{ double hocico(int x, int y); }Tu si môžeme povedať, že názov rozhrania a názov metódy v rozhraní nie je dôležitý. Dôležité je, aby sedeli vlastnosti. Teda rozhranie má len jednu metódu (mimo default metód) a metóde sedí typ návratovej hodnoty a parametre metódy. Implemetačnú triedu už teda nepotrebujeme, lebo akoby implementáciou je lambda výraz. Môžem si to nechať, ak to potrebujem takto používať, ale v našom prípade nepotrebujem implementačnú triedu. Lambda výraz sa správa ako implemtnácia rozhrania. Ale nie je to implementácia. Tento príklad bude fungovať: HelloWord3 helloWord = new HelloWord3(); IHelloWord helloWord1 = new HelloWordImpl(); IHelloWord helloWord2 = () -> System.out.println("HelloWord impls lambda"); helloWord1.sayHello(); helloWord2.sayHello();Na výstupe bude: HelloWord impls HelloWord impls lambda Pokračovanie nabudúce 😗 Mrkni zatiaľ moje kurzy o Java programovaní alebo videá, ktoré máme na Youtube o Lambda výrazoch: https://www.youtube.com/watch?v=tzSFOgnDZZo&list=PL5dKLQR6-HyU7jyoTuZGFmPLAEFpIumi4
Tipy a triky
04.08.2019
Skillmea tím

Tipy, triky a chyby v jazyku C++ pre začiatočníkov

Týmto článkom by som vám chcel predstaviť zaujímavé tipy a triky v jazyku C++, ktoré by ste mohli použiť vo vašom kóde. Sú veľmi jednoduché, pretože sú určené pre začiatočníkov. Navyše by som vás chcel upozorniť na niektoré často sa opakujúce chyby, ktoré sa vo vašom kóde môžu na začiatku vyskytovať. Nejedná sa o chybu v pravom zmysle slova, teda nie takú, po ktorej by bol váš kód nepreložiteľný, skôr sa jedná o obrúsenie vášho programátorského štýlu, či vytvorenie takého kódu, ktorý bude rýchlo vykonávaný. Poslednú spomínanú vlastnosť dosiahnete s C++ ľahko, pretože kódy, ktoré navrhnete v C++ sa vykonajú oveľa rýchlejšie ako tie, ktoré navrhnete v iných jazykoch. Viem programovať vo viacerých jazykoch a preto to mám skutočne odskúšané. Pamätajte, že C++ to však nevykoná za vás, pretože aj tam sa dá vytvoriť veľmi špatný kód. Zámerne so použil termín špatný, hoci nie je odborný. Myslím tým kód, v ktorom zlým štýlom a technikou nedosiahnete požiadavky, ktoré sa na kód kladú. Príkladom môže byť práve rýchlosť vykonávania spustiteľného kódu, jeho prehľadnosť, či ľahká udržiavateľnosť. V nasledujúcich riadkoch vám to na pár príkladoch ozrejmím. Príklady tipov, trikov a chýbUrčite ste sa už v programovaní pokúšali naprogramovať jednoduché matematické operácie. Majme teda nasledovný kód: #include <iostream> int main() { int a; int b = -1; int c; int d = 4; int e = 2; int f = 3; int g; a = b + c; d = e - f; g = a * d; std::cout << "g = " << std::endl; std::cin.get(); std::cin.get(); return 0; }Najprv by som sa vás chcel spýtať, či sa vám takto napísaný kód na prvý pohľad páči. Myslím tým po koncepčnej stránke. Prvou chybou je, že začínajúci programátori neodsadzujú bloky kódu. Tak napríklad tento kód by sa dal pekne odsadiť pomocou nasledujúcich pravidiel. Oddeľte direktívy preprocesora od hlavičky funkcii main(). Oddeľte deklarácie a definície premenných od zvyšku kódu. Niekedy, keď budete používať veľa premenných, môžete aj jednotlivé deklarácie premenných usporiadať do logických celkov. Môžete vytvoriť bloky kódu podľa typu premennej. Navyše, jazyk C++ vám umožňuje deklarovať a inicializovať premennú namieste, kde to skutočne potrebujete, čiže predtým, ako ju použijete. Ďalej, v tomto kóde oddeľte nosnú časť kódu a to vytvorte blok kódu, kde sa vykonávajú jednotlivé matematické operácie. Nakoniec, oddeľte zápis na obrazovku a tiež načítanie zo vstupu klávesnice. Potom už len oddelíte kľúčové slovo return s jeho návratovou hodnotou. Po spomenutých úpravách vám vznikne čitateľný kód, ktorý vyzerá nasledovne: #include <iostream> int main() { int a; int b = -1; int c; int d = 4; int e = 2; int f = 3; int g; a = b + c; d = e - f; g = a * d; std::cout << "g = " << std::endl; std::cin.get(); std::cin.get(); return 0; }Keď sa ďalej pozrieme na kód, môžeme niektoré deklarácie umiestniť na jeden riadok. Konkrétne prevedenie nechám na vás, ale ja by som odporúčal deklarovať na jednom riadku premenné, ktoré sa neinicializujú hneď na začiatku súčasne s deklaráciou. Na druhý riadok by som umiestnil premenné, ktoré sa inicializujú súčasne s deklaráciou. Takto získate ešte väčší prehľad v kóde a ušetríte 4 riadky kódu. Kód bude vyzerať nasledovne: #include <iostream> int main() { int a, c, g; int b = -1, d = 4, e = 2, f = 3; a = b + c; d = e - f; g = a * d; std::cout << "g = " << std::endl; std::cin.get(); std::cin.get(); return 0; }Teraz prejdeme k spomínanej rýchlosti. Je mi jasné, že pri tak krátkom kóde ušetríme relatívne málo času, ale keby sa nosná časť kódu, čiže tri matematické operácie s priraďovaním, vykonávali v cykle napr. 1 000 000 krát, videli by ste zaručene rozdiel. V uvedenom príklade nie je nutné použiť 7 premenných a výsledok vyhodnocovať na trikrát. Výsledky  b + c a  e - f  sa vynásobia a priradia do ďalšej premennej. Tak, ako to je naprogramované, je to zbytočné. Skúste všetko vyhodnotiť ako jeden výraz a priradiť na jednom riadku. Vznikne nám kód, ktorý bude mať o dva riadky menej a bude vykonávať to isté. A v čom je vlastne problém. No v operátore priradenia. Táto operácia je príliš časovo náročná. V podstate sa musí presunúť hodnota premennej uložená na jednom pamäťovom mieste do pamäťového miesta, ktoré je určené pre inú premennú. A po kurze už viete, že počítač pozná len 0 a 1. Organizačne existuje najmenej jeden bajt. Nespomínal som síce, čo je zásobník, ale keď sa pohybujeme v jeho pamäti, trvá to ešte dlhšie. Vráťme sa ale späť, po úprave bude kód vyzerať nasledovne: #include <iostream> int main() { int g; int b = -1, d = 4, e = 2, f = 3; g = (b + c) * (e - f); std::cout << "g = " << std::endl; std::cin.get(); std::cin.get(); return 0; }A poďme ešte ďalej. Na čo vôbec v tomto kóde používame premenné, keď ich nenačítavame s vstupu. Výsledok kombinácie matematických operácií môžeme predsa zapísať rovno na obrazovku, bez toho, aby sme hodnoty priraďovali do premenných. Odstránime tým aj deklarácie. Po konečnej úprave, bude kód vyzerať nasledovne: #include <iostream> int main() { std::cout << "res = " << (-1 + 4) * (2 - 3) << std::endl; std::cin.get(); std::cin.get(); return 0; }Záverom by som teda znovu chcel zdôrazniť, aké sú programátorský štýl a technika dôležité. Vidíte, že z 18 riadkového kódu, ktorý sme získali odsadením pôvodného kódu, nám po niekoľkých úpravách zostal kód, ktorý má 8 riadkov.  A tento kód, hoci je malý, je laicky povedané, pekný. To znamená, že sa jeho spustiteľný kód vykoná rýchlo, je prehľadný a ľahko udržiavateľný. Autorom blogu je Marek Šurka, ktorý má na Learn2Code online kurz C++ pre začiatočníkov.
Tipy a triky
21.07.2019
Skillmea tím

Lambda výrazy v Jave - časť I.

V tomto a v nasledujúcich článkoch sa pozrieme na zúbok lambda výrazom. Budeme si ich vysvetľovať úplne dopodrobna, aby sme ich pochopili a využívali. Obsahovo sa zameriame na tieto oblasti: 2. porozumenie lambda výrazom, 4. použitie lambda výrazov, 6. funkcionálne rozhrania (functional intefaces), 8. referencie metód (method references), 10. vylepšenia na kolekciách. Prečo použiť lambda výrazy? Povieme si pár odrážok, prečo ich používať. • povoľuje použiť takzvané funkcionálne programovanie, čo je doteraz niečo, povedal by som čudné, keďže Java je objektovo orientovaný jazyk, • sprehľadňujú kód, lepšia čitateľnosť v niektorých prípadoch, kde by sme použili viacero zbytočných riadkov, aby sme napísali to isté. Možno sa zamýšľaš, prečo používať funkcionálne programovanie v jazyku, ktorý je objektovo orientovaný. Už nie je OOP tak dobré? Už zanikne? Nie, nezanikne a java je a myslím si, že stále bude objektovo orientovaný jazyk. Toto funkcionálne programovanie ber len ako ďalší nástroj, ktorý ako vývojár máš vo svojej ruke. Pri OOP sú vývojári zvyknutí rozmýšľať v podstatných menách, v objektoch, v triedach. Napríklad Pes šteká. Štekanie je súčasťou Psa. Tým pádom metóda, ktorá bude zabezpečovať psie štekanie, je súčasťou triedy Pes. Niekedy ale potrebujem kus kódu, metódu – alebo inak povedané funkciu, ktorá nepatrí do žiadnej triedy špeciálne. Podsunutie chovania do metódyUvažuj nad tým, že máš metódu, ktorá na konzolu vypíše nejaký text. Napríklad staré známe Hello World. Pre tento účel by sme si vytvorili triedu, ktorej by bola metóda na vypísanie Hello Word. Túto metódu by sme potom vypísali na konzolu v main metóde. Príklad v idea Lambda2. Našou úlohou bude teraz prerobiť tento kód tak, aby som danej metóde podsunul správanie a vo vnútri tej metódy sa len vykoná to správanie. Ukážme si na príklade. Takže sme spravili, čo sme chceli. Do metódy sme podsunuli správanie ako argument a potom sme ho vykonali.  Ale nie tak presne. Do metódy sme podsunuli niečo, čo má v sebe správanie. Podsunuli sme implementáciu rozhrania, ktorá má v sebe metódu, ktorá vykoná očakávané správanie sa. Práve tomuto chcú lambda výrazy zabrániť. Chcú zabrániť tomu, aby sme podsúvali objekty, ale chcú, aby sme podsúvali funkcie. Namiesto tohto: public void printHelloWord(IHelloWord helloWord){ helloWord.sayHello(); }chceme do metódy vložiť nejakú akciu, nejakú funkciu. Tento prístup umožňuje správať sa k funkciám ako k hodnotám. public void printHelloWord(funkcia){ funkcia(); }Ak napíšem String meno = “Jaro”;  tak som hodnotu Jaro pridelil do premennej meno. Teraz sme ale načrtli, že do nejakej premennej by sme chceli vložiť blok kódu, ktorý prezentuje našu funkciu. Takže blok kód by sa stal hodnotou a tá by sa dala vložiť do premennej. Takže tam, kde používam danú premennú, tak tam používam aj danú funkciu, ktorá je v nej.  Pre predstavivosť, chceme dosiahnuť toto: premennaSFunkciou = public void sayHello() { System.out.println("HelloWord impls"); } Toto je možné pomocou lambda výrazov. Najprv sa ale pozrime na tento kus kódu a povedzme si, čo nepotrebujeme: • public – označuje mi, či je niečo verejne dostupné mimo triedy, dáva zmysel v kontexte triedy, tu ale prideľujeme do premennej, tak to nepotrebujeme, lebo funkcia je dostupná tomu, kto pracuje s danou premennou. premennaSFunkciou = void sayHello() { System.out.println("HelloWord impls"); }• názov sayHello = ak pristupujeme k hodnote, ktorá je v premennej, tak ku nej pristupujme názvom premennej, v našom prípade je názov premennej premennaSFunkcou, takže ani druhé meno nepotrebujeme. premennaSFunkciou = void () { System.out.println("HelloWord impls"); }• typ návratovej hodnoty – pri písaní lambda výrazov nemusím písať, aký je návratový typ, prekladač vie, podľa vnútra metódy, čo sa vracia. premennaSFunkciou = () { System.out.println("HelloWord impls"); }Toto ale ešte nie je labmda výraz. Ak napíšem šípku (pomlčka - a znamienko väčšie >) medzi zátvorky a blok kódu, tak vtedy sme vytvorili labmda výraz. premennaSFunkciou = () -> { System.out.println("HelloWord impls"); } Ak metóda obsahuje len jeden riadok, teda nie viac riadkov, tak je možné ďalej upraviť tento výraz a to tak, že odstránime zložené zátvorky. Ak je viac riadkov, tak zložené zátvorky ponecháme. premennaSFunkciou = () -> System.out.println("HelloWord impls");Teraz si už vieme predstaviť, spraviť, to, že pošleme funkciu ako parameter metódy a vo vnútri spustíme danú funkciu. public void printHelloWord(------){ -----(); }Do metódy môžeme vložiť ako argument pri volaní metódy priamo lambda výraz. printHelloWord(() -> System.out.println("HelloWord impls")){PríkladyNapíš metódu, ktorá zoberie ako parameter číslo a vynásobí ho 5timi. nasobokPiatichFunkcia = public int nasobokPiatich(int i){ return i*5; }Prepíšeme to na lambda výraz, vyškrtám všetko, čo nepotrebujem. Teda názov, návratovú hodnotu a modifikátor prístupu. nasobokPiatichFunkcia = (int i){ return i*5; }Napíšeme tam šípku a keďže riadok je tam len jeden, tak vieme odmazať kučeravé zátvorky. nasobokPiatichFunkcia = (int i) -> return i*5;Tu máme ďalšiu pomôcku, alebo možnosť škrtať. Keďže java kompilátor pozná vnútro metódy a vie, čo má vrátiť, môžem vymazať aj return. nasobokPiatichFunkcia = (int i) -> i*5;Keď máme jednoriadkový lambda výraz bez zložených zátvoriek, tak je nevyhnuté nepoužívať return. SčítaniescitanieFunkcia = (int a, int b) -> a+b; OdčítanieodcitanieFunkcia = (int a, int b) -> a-b; Bezpečné delenie bezpecneDelenieFunkcia = (int a, int b) -> { if(b==0) { return 0 ; } return a/b; };Spojenie reťazcovstringJoin = (String x, String y) -> x.concat(y); Stále sme v Jave. Teda v typovom jazyku. Aké sú typy týchto premenných, ktoré v sebe držia lambda výrazy? Video: Ak ťa viac baví počúvať a pozerať, tak si môžeš pozrieť sériu videí o lambda výrazoch v kurze Java pre pokročilých. Záver Ak by si sa chcel dozvedieť o Jave viac alebo si nepochopil všetko, tak som aj pre teba pripravil online kurzy o Jave na https://skillmea.sk. Ak sa chceš o mne dozvedieť viac, tak klikaj na jaroslavbeno.sk alebo ma sleduj na sociálnych sieťach – youtube, facebook, instagram, linkedin. Zakomponujem aj malú reklamu. V spolupráci s tvorcami stoličky Neseda.com ti ponúkam s kódom/kupónom JaroslavBeno 10% zľavu (aplikovateľná aj na zľavnenú stoličku). Ja som Jaro a my sa vidíme, počujeme ak Boh dá nabudúce. Čaves.

Nezmeškaj info o nových kurzoch a špeciálnych ponukách