Uvod u Vavr

1. Pregled

U ovom ćemo članku istražiti što je točno Vavr, zašto nam je potreban i kako ga koristiti u našim projektima.

Vavr je a funkcionalna knjižnica za Java 8+ koja pruža nepromjenjive tipove podataka i funkcionalne upravljačke strukture.

1.1. Ovisnost Mavena

Da biste koristili Vavr, morate dodati ovisnost:

 io.vavr vavr 0.9.0 

Preporučuje se uvijek koristiti najnoviju verziju. Možete ga dobiti slijedeći ovaj link.

2. Opcija

Glavni cilj Option je eliminirati null provjere u našem kodu koristeći sustav Java tipa.

Opcija je spremnik predmeta u Vavr-u sa sličnim krajnjim ciljem poput Izbornog u Javi 8. Vavr Opcija provodi Serijalizirati, Iterable, i ima bogatiji API.

Budući da bilo koja referenca objekta u Javi može imati null vrijednosti, obično moramo provjeriti nije li ništa ako izjave prije nego što ga upotrijebite. Ove provjere čine kod robusnim i stabilnim:

@Test javna praznina givenValue_whenNullCheckNeeded_thenCorrect () {Object possibleNullObj = null; if (possibleNullObj == null) {possibleNullObj = "someDefaultValue"; } assertNotNull (mogućeNullObj); }

Bez provjera, aplikacija se može srušiti zbog jednostavnog NPE:

@Test (očekuje se = NullPointerException.class) javna praznina givenValue_whenNullCheckNeeded_thenCorrect2 () {Object possibleNullObj = null; assertEquals ("somevalue", possibleNullObj.toString ()); }

Međutim, provjere čine kôd opširno i ne tako čitljivo, posebno kada ako izrazi se na kraju ugnijezde više puta.

Opcija rješava ovaj problem potpunim uklanjanjem nula i zamjenjujući ih valjanom referencom objekta za svaki mogući scenarij.

S Opcija a null vrijednost će procijeniti na primjerak Nijedna, dok će ne-null vrijednost procijeniti na primjerak Neki:

@Test javna praznina givenValue_whenCreatesOption_thenCorrect () {Opcija noneOption = Option.of (null); Opcija someOption = Option.of ("val"); assertEquals ("Ništa", noneOption.toString ()); assertEquals ("Neki (val)", someOption.toString ()); }

Stoga, umjesto da izravno koristite vrijednosti objekata, poželjno je umotati ih u Opcija primjer kao što je gore prikazano.

Primijetite, nismo morali obaviti provjeru prije pozivanja toString ipak nismo morali imati posla s a NullPointerException kao i prije. Opcije toString vraća nam značajne vrijednosti u svakom pozivu.

U drugom isječku ovog odjeljka trebao nam je null check, u kojem bismo varijabli dodijelili zadanu vrijednost, prije nego što bismo je pokušali koristiti. Opcija mogu se nositi s tim u jednom retku, čak i ako postoji null:

@Test javna praznina givenNull_whenCreatesOption_thenCorrect () {Naziv niza = null; Option nameOption = Option.of (name); assertEquals ("baeldung", nameOption.getOrElse ("baeldung")); }

Ili ne-null:

@Test javna praznina givenNonNull_whenCreatesOption_thenCorrect () {String name = "baeldung"; Option nameOption = Option.of (name); assertEquals ("baeldung", nameOption.getOrElse ("notbaeldung")); }

Primijetite kako, bez null provjere, možemo dobiti vrijednost ili vratiti zadani u jednom retku.

3. Korijen

U Javi ne postoji izravni ekvivalent strukturi podataka korice. Tuple je čest pojam u funkcionalnim programskim jezicima. Korte su nepromjenjive i mogu sadržavati više objekata različitih vrsta na siguran način.

Vavr donosi torte na Javu 8. Korice su tipa Tuple1, Tuple2 do Korijen8 ovisno o broju elemenata koje trebaju poduzeti.

Trenutno postoji gornja granica od osam elemenata. Pristupamo elementima korice poput korijen._n gdje n sličan je pojmu indeksa u nizovima:

javna praznina whenCreatesTuple_thenCorrect1 () {Tuple2 java8 = Tuple.of ("Java", 8); String element1 = java8._1; int element2 = java8._2 (); assertEquals ("Java", element1); assertEquals (8, element2); }

Primijetite da se prvi element dohvaća s n == 1. Dakle, tuple ne koristi nultu bazu poput niza. Tipovi elemenata koji će biti pohranjeni u tuple moraju biti deklarirani u svojoj deklaraciji tipa kako je prikazano gore i dolje:

@Test public void whenCreatesTuple_thenCorrect2 () {Tuple3 java8 = Tuple.of ("Java", 8, 1.8); String element1 = java8._1; int element2 = java8._2 (); dvostruki element3 = java8._3 (); assertEquals ("Java", element1); assertEquals (8, element2); assertEquals (1.8, element3, 0.1); }

Tuple je mjesto u spremanju fiksne grupe objekata bilo koje vrste koji se bolje obrađuju kao jedinica i mogu se prenositi. Očitiji slučaj upotrebe vraća više od jednog objekta iz funkcije ili metode na Javi.

4. Pokušajte

U Vavr, Probati je spremnik za proračunšto može rezultirati iznimkom.

Kao Opcija obavija poništavajući objekt tako da se ne moramo izričito brinuti nula s ako čekovi, Probati obavija izračunavanje tako da ne moramo izričito voditi računa o iznimkama pokušaj uhvatiti blokovi.

Uzmimo za primjer sljedeći kod:

@Test (očekuje se = ArithmeticException.class) javna praznina givenBadCode_whenThrowsException_thenCorrect () {int i = 1/0; }

Bez pokušaj uhvatiti blokova, aplikacija bi pala. Da biste to izbjegli, izjavu biste trebali zamotati u pokušaj uhvatiti blok. S Vavrom možemo isti kod umotati u Probati instance i dobiti rezultat:

@Test javna praznina givenBadCode_whenTryHandles_thenCorrect () {Try result = Try.of (() -> 1/0); assertTrue (result.isFailure ()); }

Je li izračunavanje bilo uspješno ili ne, tada se može provjeriti izborom u bilo kojem trenutku koda.

U gornjem isječku odlučili smo jednostavno provjeriti ima li uspjeha ili neuspjeha. Možemo odabrati i vraćanje zadane vrijednosti:

@Test javna praznina givenBadCode_whenTryHandles_thenCorrect2 () {Pokušajte s izračunavanjem = Try.of (() -> 1/0); int errorSentinel = result.getOrElse (-1); assertEquals (-1, errorSentinel); }

Ili čak izričito izbaciti iznimku po našem izboru:

@Test (očekuje se = ArithmeticException.class) javna praznina givenBadCode_whenTryHandles_thenCorrect3 () {Pokušaj rezultat = Try.of (() -> 1/0); result.getOrElseThrow (ArithmeticException :: novo); }

U svim gore navedenim slučajevima imamo kontrolu nad onim što se događa nakon izračuna, zahvaljujući Vavrovim Probati.

5. Funkcionalna sučelja

Dolaskom Jave 8 funkcionalna sučelja ugrađena su i jednostavnija za upotrebu, posebno u kombinaciji s lambdama.

Međutim, Java 8 pruža samo dvije osnovne funkcije. Uzima se samo jedan parametar i daje se rezultat:

@Test javna praznina givenJava8Function_whenWorks_thenCorrect () {Funkcija kvadrat = (broj) -> num * num; int rezultat = square.apply (2); assertEquals (4, rezultat); }

Drugi uzima samo dva parametra i daje rezultat:

@Test javna praznina givenJava8BiFunction_whenWorks_thenCorrect () {BiFunction sum = (num1, num2) -> num1 + num2; int rezultat = sum.apply (5, 7); assertEquals (12, rezultat); }

S druge strane, Vavr proširuje ideju funkcionalnih sučelja u Javi podržavajući do najviše osam parametara i začinjavajući API metodama za pamćenje, sastavljanje i kariranje.

Baš kao i korice, i ova se funkcionalna sučelja imenuju prema broju parametara koje uzimaju: Funkcija0, Funkcija1, Funkcija2 itd. S Vavrom bismo gornje dvije funkcije napisali ovako:

@Test javna praznina givenVavrFunction_whenWorks_thenCorrect () {Function1 square = (num) -> num * num; int rezultat = square.apply (2); assertEquals (4, rezultat); }

i to:

@Test javna praznina givenVavrBiFunction_whenWorks_thenCorrect () {Function2 sum = (num1, num2) -> num1 + num2; int rezultat = sum.apply (5, 7); assertEquals (12, rezultat); }

Kada ne postoji parametar, ali i dalje trebamo izlaz, u Javi 8 trebamo koristiti a Potrošač tipa, u Vavr Funkcija0 postoji li pomoć:

@Test javna void kadaCreatesFunction_thenCorrect0 () {Function0 getClazzName = () -> this.getClass (). GetName (); Niz clazzName = getClazzName.apply (); assertEquals ("com.baeldung.vavr.VavrTest", clazzName); }

Što kažete na funkciju s pet parametara, pitanje je samo upotrebe Funkcija5:

@Test public void whenCreatesFunction_thenCorrect5 () {Function5 concat = (a, b, c, d, e) -> a + b + c + d + e; Niz finalString = concat.apply ("Pozdrav", "svijet", "!", "Nauči", "Vavr"); assertEquals ("Pozdrav svijete! Uči Vavr", finalString); }

Također možemo kombinirati statičku tvorničku metodu FunkcijaN.of za bilo koju funkciju za stvaranje Vavr funkcije iz reference metode. Kao da imamo sljedeće iznos metoda:

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

Iz nje možemo stvoriti funkciju ovako:

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

6. Zbirke

Tim Vavr uložio je puno truda u dizajniranje novog API-ja zbirki koji udovoljava zahtjevima funkcionalnog programiranja, tj. Postojanosti, nepromjenjivosti.

Java kolekcije su promjenjive, što ih čini izvrsnim izvorom neuspjeha programa, posebno u prisutnosti istodobnosti. The Kolekcija sučelje pruža metode poput ove:

Zbirka sučelja {void clear (); }

Ova metoda uklanja sve elemente u zbirci (proizvodeći nuspojavu) i ne vraća ništa. Razredi poput ConcurrentHashMap stvoreni su za rješavanje već stvorenih problema.

Takva klasa ne samo da dodaje nula graničnih koristi, već i pogoršava performanse klase čije rupe pokušava popuniti.

Uz nepromjenjivost, besplatno dobivamo zaštitu od niti: nema potrebe za pisanjem novih predavanja za rješavanje problema koji uopće ne bi trebao postojati.

Druge postojeće taktike za dodavanje nepromjenjivosti zbirkama u Javi i dalje stvaraju više problema, naime, iznimke:

@Test (očekuje se = UnsupportedOperationException.class) javna praznina whenImmutableCollectionThrows_thenCorrect () {java.util.List wordList = Arrays.asList ("abracadabra"); java.util.List list = Collections.unmodifiableList (wordList); list.add ("bum"); }

Svi gore navedeni problemi ne postoje u zbirkama Vavr.

Da biste stvorili popis u Vavru:

@Test public void whenCreatesVavrList_thenCorrect () {List intList = List.of (1, 2, 3); assertEquals (3, intList.length ()); assertEquals (novi Integer (1), intList.get (0)); assertEquals (novi Integer (2), intList.get (1)); assertEquals (novi Integer (3), intList.get (2)); }

API-ji su također dostupni za izvršavanje izračuna na postojećem popisu:

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

Vavr kolekcije nude većinu uobičajenih klasa koje se nalaze u okviru Java Collections Framework i zapravo su sve značajke implementirane.

Za poneti je nepromjenljivost, uklanjanje praznih vrsta povratka i API-ji koji proizvode nuspojave, bogatiji skup funkcije za rad na temeljnim elementima, vrlo kratak, robustan i kompaktni kod u usporedbi s operacijama prikupljanja Java.

Potpuna pokrivenost Vavr kolekcija izvan je dosega ovog članka.

7. Provjera valjanosti

Vavr donosi koncept Aplikativni faktor na Javu iz svijeta funkcionalnog programiranja. Najjednostavnije rečeno, an Aplikativni faktor omogućuje nam izvođenje niza radnji tijekom zbrajanja rezultata.

Razred vavr.control.Potvrđivanje olakšava nakupljanje pogrešaka. Imajte na umu da se program obično prekida čim se naiđe na pogrešku.

Međutim, Provjera valjanosti nastavlja obrađivati ​​i akumulirati pogreške kako bi program na njih djelovao kao paket.

Uzmite u obzir da korisnike registriramo do Ime i dob i prvo želimo uzeti sve unose i odlučiti hoćemo li stvoriti Osoba instancu ili vratiti popis pogrešaka. Ovdje je naš Osoba razred:

javni razred Osoba {ime privatnog niza; privatno int doba; // standardni konstruktori, postavljači i getteri, toString}

Dalje kreiramo klasu koja se zove PersonValidator. Svako polje potvrdit će jedna metoda, a drugo se može koristiti za kombiniranje svih rezultata u jedno Provjera valjanosti primjer:

klasa PersonValidator {String NAME_ERR = "Nevažeći znakovi u imenu:"; String AGE_ERR = "Dob mora biti najmanje 0"; javna provjera valjanosti validatePerson (ime niza, int age) {return Validation.combine (validateName (ime), validateAge (dob)). ap (Person :: new); } private Validation validateName (naziv niza) {String invalidChars = name.replaceAll ("[a-zA-Z]", ""); vratiti invalidChars.isEmpty ()? Validation.valid (name): Validation.invalid (NAME_ERR + invalidChars); } private Validation validateAge (int age) {return age <0? Validation.invalid (AGE_ERR): Validation.valid (dob); }}

Pravilo za dob je da to treba biti cijeli broj veći od 0 i pravilo za Ime jest da ne smije sadržavati posebne znakove:

@Test public void whenValidationWorks_thenCorrect () {PersonValidator personValidator = novi PersonValidator (); Provjera valjanosti valjano = personValidator.validatePerson ("John Doe", 30); Provjera valjanosti invalid = personValidator.validatePerson ("John? Doe! 4", -1); assertEquals ("Važeće (Osoba [ime = John Doe, dob = 30])", valid.toString ()); assertEquals ("Nevaljano (Popis (Nevažeći znakovi u imenu:?! 4, Dob mora biti najmanje 0))", invalid.toString ()); }

Važeća vrijednost sadržana je u Provjera valjanosti Primjerice, popis pogrešaka provjere valjanosti nalazi se u a Provjera valjanosti.Nevaljano primjer. Dakle, svaka metoda provjere valjanosti mora vratiti jednu od dvije.

Iznutra Provjera valjanosti je instanca Osoba dok je unutra Provjera valjanosti.Nevaljano je popis pogrešaka.

8. Lijen

Lijen je spremnik koji predstavlja vrijednost izračunatu lijeno, tj. izračunavanje se odgađa dok se ne zahtijeva rezultat. Nadalje, procijenjena vrijednost se predmemorira ili pamti i vraća iznova i iznova svaki put kad je to potrebno bez ponavljanja izračuna:

@Test javna praznina givenFunction_whenEvaluatesWithLazy_thenCorrect () {Lijen lijen = Lijen.of (Matematika :: slučajan); assertFalse (lijen.isEvaluated ()); dvostruki val1 = lijen.get (); assertTrue (lazy.isEvaluated ()); dvostruki val2 = lijen.get (); assertEquals (val1, val2, 0,1); }

U gornjem primjeru funkcija koju procjenjujemo je Matematika. random. Primijetite da u drugom retku provjeravamo vrijednost i shvaćamo da funkcija još nije izvršena. To je zato što još uvijek nismo pokazali interes za povratnu vrijednost.

U trećem retku koda pozivanjem pokazujemo interes za vrijednost izračuna Lijen.dobiti. U ovom trenutku funkcija izvršava i Lijen.procijenjen vraća istina.

Također nastavljamo i potvrđujemo podsjetnik za Lijen pokušajem da dobiti opet vrijednost. Kad bi se funkcija koju smo pružili ponovno izvršila, definitivno bismo dobili drugi slučajni broj.

Međutim, Lijen opet lijeno vraća početno izračunatu vrijednost kao potvrdu konačne tvrdnje.

9. Podudaranje uzoraka

Usklađivanje uzoraka izvorni je pojam u gotovo svim funkcionalnim programskim jezicima. Za sada toga nema u Javi.

Umjesto toga, kad god želimo izvršiti izračunavanje ili vratiti vrijednost na temelju ulaznih podataka koje primimo, koristimo ih višestruko ako izjave za rješavanje ispravnog koda za izvršavanje:

@Test javna praznina whenIfWorksAsMatcher_thenCorrect () {int input = 3; Izlazni niz; if (input == 0) {output = "nula"; } if (input == 1) {output = "jedan"; } if (input == 2) {output = "dva"; } if (input == 3) {output = "tri"; } else {izlaz = "nepoznato"; } assertEquals ("tri", izlaz); }

Iznenada možemo vidjeti kôd koji obuhvaća više redaka dok samo provjeravamo tri slučaja. Svaka provjera zauzima tri retka koda. Što ako bismo morali provjeriti do stotinu slučajeva, to bi bilo oko 300 redaka, ne lijepo!

Druga alternativa je korištenje a sklopka izjava:

@Test javna void whenSwitchWorksAsMatcher_thenCorrect () {int input = 2; Izlazni niz; prekidač (ulaz) {slučaj 0: izlaz = "nula"; pauza; slučaj 1: izlaz = "jedan"; pauza; slučaj 2: izlaz = "dva"; pauza; slučaj 3: izlaz = "tri"; pauza; zadana vrijednost: output = "nepoznato"; pauza; } assertEquals ("dva", izlaz); }

Ništa bolje. I dalje u prosjeku trošimo 3 retka po provjeri. Puno zbunjenosti i potencijalnih grešaka. Zaboravljajući a pauza klauzula nije problem u vrijeme kompajliranja, ali kasnije može rezultirati teško prepoznatljivim bugovima.

U Vavr zamjenjujemo cijelu sklopka blok s Podudarnost metoda. Svaki slučaj ili ako Izjava se zamjenjuje s Slučaj prizivanje metode.

Napokon, atomski obrasci poput $() zamijeniti uvjet koji zatim procjenjuje izraz ili vrijednost. To također pružamo kao drugi parametar za Slučaj:

@Test javna void whenMatchworks_thenCorrect () {int input = 2; Izlaz niza = Podudaranje (ulaz) .of (Case ($ (1), "one"), Case ($ (2), "two"), Case ($ (3), "three"), Case ($ ( ), "?")); assertEquals ("dva", izlaz); }

Primijetite kako je kod kompaktan, u prosjeku uzimajući samo jedan redak po provjeri. API za podudaranje uzoraka puno je moćniji od ovog i može raditi složenije stvari.

Na primjer, atomske izraze možemo zamijeniti predikatom. Zamislite da raščlanjujemo naredbu konzole za Pomozite i verzija zastave:

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);})));

Neki su korisnici možda upoznati sa skraćenicom (-v), dok su drugi s punom verzijom (–verzija). Dobar dizajner mora uzeti u obzir sve ove slučajeve.

Bez potrebe za nekoliko ako izjave, pobrinuli smo se za više uvjeta.Više o predikatima, višestrukim uvjetima i nuspojavama u podudaranju uzoraka naučit ćemo u zasebnom članku.

10. Zaključak

U ovom smo članku predstavili Vavr, popularnu biblioteku funkcionalnog programiranja za Javu 8. Riješili smo glavne značajke koje možemo brzo prilagoditi kako bismo poboljšali naš kôd.

Potpuni izvorni kod za ovaj članak dostupan je u projektu Github.