Úvod do Vavr

1. Prehľad

V tomto článku sa chystáme preskúmať, čo presne je Vavr, prečo ho potrebujeme a ako ho použiť v našich projektoch.

Vavr je a funkčná knižnica pre Java 8+, ktorá poskytuje nemenné dátové typy a funkčné riadiace štruktúry.

1.1. Maven závislosť

Aby ste mohli používať Vavr, musíte pridať závislosť:

 io.vavr vavr 0.9.0 

Odporúča sa vždy používať najnovšiu verziu. Získate ho kliknutím na tento odkaz.

2. Možnosť

Hlavným cieľom Option je eliminovať nulové kontroly v našom kóde využitím systému typu Java.

Možnosť je kontajner objektov vo Vavr s podobným konečným cieľom ako voliteľný v Java 8. Vavr Možnosť náradie Serializovateľné, iterovateľné, a má bohatšie API.

Pretože akýkoľvek objektový odkaz v Jave môže mať a nulový hodnotu, zvyčajne musíme skontrolovať neplatnosť pomocou ak pred jeho použitím. Vďaka týmto kontrolám je kód robustný a stabilný:

@Test public void givenValue_whenNullCheckNeeded_thenCorrect () {Object possibleNullObj = null; if (possibleNullObj == null) {possibleNullObj = "someDefaultValue"; } assertNotNull (possibleNullObj); }

Bez kontrol môže aplikácia zlyhať kvôli jednoduchosti NPE:

@Test (očakáva sa = NullPointerException.class) public void givenValue_whenNullCheckNeeded_thenCorrect2 () {Object possibleNullObj = null; assertEquals ("somevalue", possibleNullObj.toString ()); }

Kontroly však vytvárajú kód podrobné a nie také čitateľné, najmä keď ak vyhlásenia sa nakoniec vnoria viackrát.

Možnosť tento problém rieši úplným vylúčením nuly a nahradí ich platným odkazom na objekt pre každý možný scenár.

S Možnosť a nulový hodnota sa vyhodnotí na inštanciu Žiadne, zatiaľ čo nenulová hodnota sa vyhodnotí na inštanciu Niektoré:

@Test public void givenValue_whenCreatesOption_thenCorrect () {Option noneOption = Option.of (null); Možnosť someOption = Option.of ("val"); assertEquals ("None", noneOption.toString ()); assertEquals ("Some (val)", someOption.toString ()); }

Preto namiesto priameho použitia hodnôt objektov je vhodné zabaliť ich do Možnosť napríklad ako je uvedené vyššie.

Všimnite si, že sme pred telefonovaním nemuseli robiť kontrolu natiahnuť napriek tomu sme nemuseli riešiť a NullPointerException ako sme to robili predtým. Možnosti natiahnuť vráti nám zmysluplné hodnoty pri každom hovore.

V druhom úryvku tejto časti sme potrebovali a nulový začiarknite políčko, v ktorom by sme premennej priradili predvolenú hodnotu, skôr ako sa pokúsime ju použiť. Možnosť dokáže to vyriešiť v jednom riadku, aj keď je tu null:

@ Test public void givenNull_whenCreatesOption_thenCorrect () {Názov reťazca = null; Názov možnostiOption = Option.of (name); assertEquals ("baeldung", nameOption.getOrElse ("baeldung")); }

Alebo nenulové:

@Test public void givenNonNull_whenCreatesOption_thenCorrect () {Názov reťazca = "baeldung"; Názov možnostiOption = Option.of (name); assertEquals ("baeldung", nameOption.getOrElse ("notbaeldung")); }

Všimnite si, ako, bez nulový kontroly, môžeme získať hodnotu alebo vrátiť predvolenú hodnotu v jednom riadku.

3. Tuple

V Jave neexistuje priamy ekvivalent n-tice dátovej štruktúry. N-tica je bežný pojem vo funkčných programovacích jazykoch. N-tice sú nemenné a môžu bezpečným spôsobom pojať viac objektov rôznych typov.

Vavr prináša n-tice do Javy 8. N-tice sú typového typu Tuple1, Tuple2 do Tuple8 v závislosti od počtu prvkov, ktoré majú prijať.

V súčasnosti existuje horná hranica ôsmich prvkov. Pristupujeme k prvkom n-tice násobný._n kde n je podobný pojmu index v poliach:

public void whenCreatesTuple_thenCorrect1 () {Tuple2 java8 = Tuple.of ("Java", 8); Reťazcový prvok1 = java8._1; int element2 = java8._2 (); assertEquals ("Java", prvok1); assertEquals (8, element2); }

Všimnite si, že prvý prvok je načítaný pomocou n == 1. Takže n-tica nepoužíva nulovú základňu ako pole. Typy prvkov, ktoré budú uložené v n-tici, musia byť deklarované v deklarácii typu, ako je uvedené vyššie a nižšie:

@Test public void whenCreatesTuple_thenCorrect2 () {Tuple3 java8 = Tuple.of ("Java", 8, 1,8); Reťazcový prvok1 = java8._1; int element2 = java8._2 (); double element3 = java8._3 (); assertEquals ("Java", element1); assertEquals (8, element2); assertEquals (1,8, element3, 0,1); }

Miesto n-tice je v ukladaní pevnej skupiny objektov ľubovoľného typu, ktoré sú lepšie spracované ako jednotka a dajú sa okolo nich prechádzať. Očividnejšie prípad použitia vráti viac ako jeden objekt z funkcie alebo metódy v Jave.

4. Vyskúšajte

Vo Vavri Vyskúšajte je kontajner na výpočetčo môže mať za následok výnimku.

Ako Možnosť zabalí predmet s možnou hodnotou Null, aby sme sa oň nemuseli výslovne starať nuly s ak kontroly, Vyskúšajte zalomí výpočet, aby sme sa nemuseli výslovne starať o výnimky s Skús chytiť blokov.

Vezmite si napríklad nasledujúci kód:

@Test (očakáva sa = ArithmeticException.class) public void givenBadCode_whenThrowsException_thenCorrect () {int i = 1/0; }

Bez Skús chytiť blokov, aplikácia by zlyhala. Aby ste sa tomu vyhli, bolo by potrebné zabaliť vyhlásenie do a Skús chytiť blokovať. S Vavrom môžeme zabaliť rovnaký kód do a Vyskúšajte inštancie a získajte výsledok:

@Test public void givenBadCode_when TryHandles_thenCorrect () {Try result = Try.of (() -> 1/0); assertTrue (result.isFailure ()); }

Či bol výpočet úspešný alebo nie, je možné skontrolovať podľa výberu kedykoľvek v kóde.

Vo vyššie uvedenom úryvku sme sa rozhodli jednoducho skontrolovať úspešnosť alebo neúspech. Môžeme sa tiež rozhodnúť vrátiť predvolenú hodnotu:

@Test public void givenBadCode_when TryHandles_thenCorrect2 () {Try computation = Try.of (() -> 1/0); int errorSentinel = result.getOrElse (-1); assertEquals (-1, errorSentinel); }

Alebo dokonca výslovne zavrhnúť výnimku podľa nášho výberu:

@Test (očakáva sa = ArithmeticException.class) public void givenBadCode_when TryHandles_thenCorrect3 () {vyskúšať výsledok = Try.of (() -> 1/0); result.getOrElseThrow (ArithmeticException :: new); }

Vo všetkých vyššie uvedených prípadoch máme vďaka Vavrovi kontrolu nad tým, čo sa stane po výpočte Vyskúšajte.

5. Funkčné rozhrania

S príchodom Java 8 sú zabudované a ľahšie použiteľné funkčné rozhrania, najmä v kombinácii s lambdami.

Java 8 však poskytuje iba dve základné funkcie. Jeden vezme iba jeden parameter a prinesie výsledok:

@Test public void givenJava8Function_whenWorks_thenCorrect () {Funkcia square = (num) -> num * num; int výsledok = štvorec. použiť (2); assertEquals (4, výsledok); }

Druhý má iba dva parametre a vedie k výsledku:

@Test public void givenJava8BiFunction_whenWorks_thenCorrect () {BiFunction sum = (num1, num2) -> num1 + num2; int vysledok = sum.apply (5, 7); assertEquals (12, výsledok); }

Na druhú stranu, Vavr rozširuje myšlienku funkčných rozhraní v Jave tým, že podporuje až osem parametrov a API obohacuje metódami memoizácie, kompozície a kari.

Rovnako ako n-tice sú aj tieto funkčné rozhrania pomenované podľa počtu parametrov, ktoré berú: Funkcia0, Funkcia1, Funkcia2 atď. S Vavr by sme napísali vyššie uvedené dve funkcie, ako je táto:

@ Test public void givenVavrFunction_whenWorks_thenCorrect () {Funkcia 1 štvorec = (počet) -> počet * počet; int výsledok = štvorec. použiť (2); assertEquals (4, výsledok); }

a to:

@ Test public void givenVavrBiFunction_whenWorks_thenCorrect () {Function2 sum = (num1, num2) -> num1 + num2; int vysledok = sum.apply (5, 7); assertEquals (12, výsledok); }

Ak neexistuje žiadny parameter, ale stále potrebujeme výstup, v prostredí Java 8 by sme museli použiť a Spotrebiteľ typu, vo Vavr Funkcia0 je tu na pomoc:

@Test public void whenCreatesFunction_thenCorrect0 () {Function0 getClazzName = () -> this.getClass (). GetName (); Reťazec clazzName = getClazzName.apply (); assertEquals ("com.baeldung.vavr.VavrTest", clazzName); }

Čo tak päťfunkčná funkcia, je to len otázka použitia Funkcia5:

@Test public void whenCreatesFunction_thenCorrect5 () {Function5 concat = (a, b, c, d, e) -> a + b + c + d + e; String finalString = concat.apply ("Hello", "world", "!", "Learn", "Vavr"); assertEquals ("Hello world! Learn Vavr", finalString); }

Môžeme tiež kombinovať statickú továrenskú metódu Funkcia č pre ktorúkoľvek z funkcií vytvorenie funkcie Vavr z odkazu na metódu. Ako keby sme mali nasledovné súčet metóda:

public int sum (int a, int b) {return a + b; }

Môžeme z toho vytvoriť funkciu takto:

@Test public void whenCreatesFunctionFromMethodRef_thenCorrect () {Function2 sum = Function2.of (this :: sum); int summed = sum.apply (5, 6); assertEquals (11, sčítané); }

6. Zbierky

Tím spoločnosti Vavr vynaložil veľa úsilia na navrhnutie nového API kolekcií, ktoré spĺňa požiadavky funkčného programovania, teda vytrvalosti, nemennosti.

Zbierky Java sú premenlivé, čo z nich robí vynikajúci zdroj zlyhania programu, najmä za prítomnosti súbežnosti. The Zbierka rozhranie poskytuje metódy ako je tento:

kolekcia rozhrania {void clear (); }

Táto metóda odstráni všetky prvky v kolekcii (vyvoláva vedľajší efekt) a nič nevracia. Triedy ako napr ConcurrentHashMap boli vytvorené na riešenie už vzniknutých problémov.

Takáto trieda neprináša iba nulové marginálne výhody, ale tiež zhoršuje výkonnosť triedy, ktorej medzery sa snaží vyplniť.

Vďaka nemennosti získame bezpečnosť vlákien zadarmo: nie je potrebné písať nové triedy, aby ste sa zaoberali problémom, ktorý by tam nemal byť.

Iné existujúce taktiky na zvýšenie nemennosti zbierok v Jave stále spôsobujú ďalšie problémy, menovite výnimky:

@Test (očakáva sa = UnsupportedOperationException.class) public void whenImmutableCollectionThrows_thenCorrect () {java.util.List wordList = Arrays.asList ("abracadabra"); java.util.List list = Collections.unmodifiableList (wordList); list.add ("boom"); }

Všetky vyššie uvedené problémy v zbierkach Vavr neexistujú.

Vytvorenie zoznamu vo Vavr:

@Test public void whenCreatesVavrList_thenCorrect () {List intList = List.of (1, 2, 3); assertEquals (3, intList.length ()); assertEquals (nové celé číslo (1), intList.get (0)); assertEquals (nové celé číslo (2), intList.get (1)); assertEquals (nové celé číslo (3), intList.get (2)); }

K dispozícii sú tiež API na vykonávanie výpočtov na danom zozname:

@ Test public void whenSumsVavrList_thenCorrect () {int sum = List.of (1, 2, 3) .sum (). IntValue (); assertEquals (6, suma); }

Kolekcie Vavr ponúkajú väčšinu bežných tried, ktoré sa nachádzajú v Java Collections Framework, a v skutočnosti sú implementované všetky funkcie.

Jedlo so sebou je nemennosť, odstránenie typov neplatných návratov a API produkujúce vedľajšie účinky, bohatšia sada funkcie pôsobiť na základné prvky, veľmi krátke, robustné a kompaktný kód v porovnaní s operáciami zhromažďovania Java.

Úplné pokrytie zbierok Vavr je nad rámec tohto článku.

7. Validácia

Vavr prináša koncept Aplikačný funktor do Java zo sveta funkčného programovania. Najjednoduchšie povedané an Aplikačný funktor nám umožňuje vykonávať postup akcií pri hromadení výsledkov.

Trieda vavr.kontrola. Validácia uľahčuje hromadenie chýb. Pamätajte, že program sa zvyčajne ukončí hneď, ako sa vyskytne chyba.

Avšak Validácia pokračuje v spracovaní a zhromažďovaní chýb, aby program na ne mohol pôsobiť ako dávka.

Zvážte, že používateľov registrujeme do názov a Vek a chceme najskôr vziať všetky vstupy a rozhodnúť sa, či vytvoríme a Osoba inštanciu alebo vrátiť zoznam chýb. Tu je náš Osoba trieda:

public class Osoba {private String name; súkromný int vek; // štandardné konštruktory, nastavovače a getre, toString}

Ďalej vytvoríme triedu s názvom PersonValidator. Každé pole bude validované jednou metódou a pomocou inej metódy je možné spojiť všetky výsledky do jednej Validácia inštancia:

class PersonValidator {String NAME_ERR = "Neplatné znaky v mene:"; Reťazec AGE_ERR = "Vek musí byť minimálne 0"; verejné overenie validatePerson (názov reťazca, int vek) {return Validation.combine (validateName (name), validateAge (age)). ap (Person :: new); } private Validation validateName (názov reťazca) {reťazec invalidChars = name.replaceAll ("[a-zA-Z]", ""); vrátiť invalidChars.isEmpty ()? Validation.valid (name): Validation.invalid (NAME_ERR + invalidChars); } private Validation validateAge (int age) {návratový vek <0? Validation.invalid (AGE_ERR): Validation.valid (vek); }}

Pravidlo pre Vek je, že by to malo byť celé číslo väčšie ako 0 a pravidlo pre názov je, že by nemal obsahovať žiadne špeciálne znaky:

@ Test public void whenValidationWorks_thenCorrect () {PersonValidator personValidator = nový PersonValidator (); Validácia valid = personValidator.validatePerson ("John Doe", 30); Validácia invalid = personValidator.validatePerson ("John? Doe! 4", -1); assertEquals ("Platné (Osoba [meno = John Doe, vek = 30])", valid.toString ()); assertEquals ("Neplatné (Zoznam (neplatné znaky v mene:?! 4, vek musí byť minimálne 0))" ", invalid.toString ()); }

Platná hodnota je obsiahnutá v a Validácia. Platné Napríklad zoznam chýb validácie je obsiahnutý v a Validácia. Neplatné inštancia. Každá metóda overenia musí teda vrátiť jednu z týchto dvoch metód.

Vo vnútri Validácia. Platné je príkladom Osoba zatiaľ čo vo vnútri Validácia. Neplatné je zoznam chýb.

8. Lenivý

Lenivý je kontajner, ktorý predstavuje lenivo vypočítanú hodnotu, t. j. výpočet je odložený, kým nie je požadovaný výsledok. Ďalej sa vyhodnocovaná hodnota ukladá do pamäte cache alebo do pamäte a vracia sa znovu a znovu zakaždým, keď je to potrebné, bez opakovania výpočtu:

@Test public void givenFunction_whenEvaluatesWithLazy_thenCorrect () {Lazy lazy = Lazy.of (Math :: random); assertFalse (lazy.isEvaluated ()); double val1 = lazy.get (); assertTrue (lazy.isEvaluated ()); double val2 = lazy.get (); assertEquals (val1, val2, 0,1); }

Vo vyššie uvedenom príklade je to funkcia, ktorú vyhodnocujeme Matematika. Náhoda. Všimnite si, že v druhom riadku skontrolujeme hodnotu a uvedomíme si, že funkcia ešte nebola vykonaná. Je to tak preto, lebo stále sme neprejavili záujem o návratnú hodnotu.

V treťom riadku kódu prejavíme záujem o výpočtovú hodnotu volaním Lazy.get. V tomto okamihu sa funkcia vykoná a Lenivý.hodnotený vracia sa pravda.

Ďalej tiež pokračujeme a potvrdzujeme pamäťový kúsok z Lenivý pokusom o dostať hodnotu znova. Ak by sa funkcia, ktorú sme poskytli, vykonala znova, určite by sme dostali iné náhodné číslo.

Avšak Lenivý opäť lenivo vráti pôvodne vypočítanú hodnotu, ako potvrdí konečné tvrdenie.

9. Zhoda vzorov

Priraďovanie vzorov je natívny koncept takmer vo všetkých funkčných programovacích jazykoch. V Jave zatiaľ nič také neexistuje.

Namiesto toho, kedykoľvek chceme vykonať výpočet alebo vrátiť hodnotu na základe prijatého vstupu, použijeme viac ak vyhlásenia na vyriešenie správneho kódu na vykonanie:

@Test public void whenIfWorksAsMatcher_thenCorrect () {int input = 3; Výstup reťazca; if (vstup == 0) {výstup = "nula"; } if (vstup == 1) {výstup = "jeden"; } if (vstup == 2) {výstup = "dva"; } if (vstup == 3) {výstup = "tri"; } else {output = "neznámy"; } assertEquals ("tri", výstup); }

Zrazu vidíme, ako sa kód rozkladá na viacerých riadkoch, zatiaľ čo len kontrolujeme tri prípady. Každá kontrola zaberá tri riadky kódu. Čo keby sme mali skontrolovať až sto prípadov, to by bolo asi 300 riadkov, nie pekné!

Ďalšou alternatívou je použitie a prepínač vyhlásenie:

@Test public void whenSwitchWorksAsMatcher_thenCorrect () {int input = 2; Výstup reťazca; switch (vstup) {case 0: output = "zero"; prestávka; prípad 1: výstup = "jeden"; prestávka; prípad 2: výstup = "dva"; prestávka; prípad 3: výstup = "tri"; prestávka; predvolené: output = "neznámy"; prestávka; } assertEquals ("dva", výstup); }

Nie o nič lepšie. Stále sme v priemere 3 riadky na kontrolu. Veľa zmätku a potenciálu pre chyby. Zabúdanie a prestávka klauzula nie je problémom v čase kompilácie, ale môže mať za následok neskôr ťažko odhaliteľné chyby.

Vo Vavri vymieňame celé prepínač blok s a Zápas metóda. Každý prípade alebo ak vyhlásenie sa nahrádza a Prípad vyvolanie metódy.

Nakoniec, atómové vzorce ako $() nahraďte podmienku, ktorá potom vyhodnotí výraz alebo hodnotu. Toto tiež poskytujeme ako druhý parameter Prípad:

@ Test public void whenMatchworks_thenCorrect () {int input = 2; Reťazcový výstup = Zhoda (vstup) .of (Prípad ($ (1), „jeden“), Prípad ($ (2), „dva“), Prípad ($ (3), „tri“), Prípad ($ ( ), „?“)); assertEquals ("dva", výstup); }

Všimnite si, aký je kód kompaktný, priemerne na jednu kontrolu dáva iba jeden riadok. Rozhranie API na porovnávanie vzorov je oveľa výkonnejšie ako toto a dokáže robiť zložitejšie veci.

Napríklad môžeme nahradiť atómové výrazy predikátom. Predstavte si, že analyzujeme príkaz konzoly Pomoc a verzia vlajky:

Match (arg) .of (Case ($ (isIn ("- h", "--help")), o -> run (this :: displayHelp)), Case ($ (isIn ("- v", " --version ")), o -> run (this :: displayVersion)), Case ($ (), o -> run (() -> {throw new IllegalArgumentException (arg);})));

Niektorí používatelia môžu lepšie poznať skratkovú verziu (-v), zatiaľ čo iní plnú verziu (–verzia). Dobrý dizajnér musí zvážiť všetky tieto prípady.

Bez potreby viacerých ak vyhlásení sme sa postarali o viac podmienok.Viac informácií o predikátoch, viacerých stavoch a vedľajších účinkoch pri porovnávaní vzorov sa dozvieme v samostatnom článku.

10. Záver

V tomto článku sme predstavili Vavr, populárnu funkcionálnu programovaciu knižnicu pre Java 8. Riešili sme hlavné funkcie, ktoré môžeme rýchlo prispôsobiť na vylepšenie nášho kódu.

Celý zdrojový kód tohto článku je k dispozícii v projekte Github.


$config[zx-auto] not found$config[zx-overlay] not found