Vodič za Java BiFunction sučelje

1. Uvod

Java 8 uvela je programiranje funkcionalnog stila, omogućujući nam parameterizaciju općenitih metoda predavanjem funkcija.

Vjerojatno smo najpoznatiji s jednoparametarskim Java 8 funkcionalnim sučeljima poput Funkcija, Predikat, i Potrošač.

U ovom uputstvu ćemo pogledati funkcionalna sučelja koja koriste dva parametra. Takve se funkcije nazivaju binarnim funkcijama i na Javi su predstavljene s BiFunction funkcionalno sučelje.

2. Jednoparametarske funkcije

Dozovimo brzo kako koristimo jednoparametarsku ili unarnu funkciju, kao što to radimo u streamovima:

Popis mapiran = Stream.of ("hello", "world") .map (word -> word + "!") .Collect (Collectors.toList ()); assertThat (preslikana) .containsExactly ("bok!", "svijet!");

Kao što vidimo, karta koristi Funkcija, koji uzima jedan parametar i omogućuje nam izvođenje operacije nad tom vrijednošću, vraćanjem nove vrijednosti.

3. Dvoparametarske operacije

Biblioteka Java Stream pruža nam smanjiti funkcija koja nam omogućuje kombiniranje elemenata toka. Moramo izraziti kako se vrijednosti koje smo do sada akumulirali transformiraju dodavanjem sljedeće stavke.

The smanjiti funkcija koristi funkcionalno sučelje BinaryOperator, koji uzima dva objekta iste vrste kao svoje ulaze.

Zamislimo da se želimo pridružiti svim stavkama u našem streamu stavljajući nove ispred s odvajačem crtica. U sljedećim ćemo odjeljcima pogledati nekoliko načina kako to primijeniti.

3.1. Koristeći Lambdu

Provedba lambda za a BiFunction ima prefiks dva parametra, okružena zagradama:

Rezultat niza = Stream.of ("hello", "world") .reduce ("", (a, b) -> b + "-" + a); assertThat (rezultat) .isEqualTo ("world-hello-");

Kao što vidimo, dvije vrijednosti, a i b jesu Žice. Napisali smo lambda koji ih kombinira da bi se dobio željeni izlaz, s tim da je prvi drugi, a između njih crtica.

To bismo trebali primijetiti smanjiti koristi početnu vrijednost - u ovom slučaju prazan niz. Tako ćemo na kraju dobiti crticu s gornjim kodom, jer je prva vrijednost iz našeg toka spojena s njom.

Također, trebali bismo primijetiti da zaključivanje tipa Java omogućuje da većinu vremena izostavimo vrste naših parametara. U situacijama kada vrsta lambde nije jasna iz konteksta, za svoje parametre možemo koristiti vrste:

Rezultat niza = Stream.of ("hello", "world") .reduce ("", (String a, String b) -> b + "-" + a);

3.2. Korištenje funkcije

Što ako bismo htjeli da gornji algoritam ne stavi crticu na kraj? Mogli bismo napisati više koda u našoj lambdi, ali to bi moglo postati neuredno. Izdvojimo umjesto toga funkciju:

private String kombinacijaWithoutTrailingDash (Niz a, Niz b) {if (a.isEmpty ()) {return b; } povratak b + "-" + a; }

A onda ga nazovite:

Rezultat niza = Stream.of ("hello", "world") .reduce ("", (a, b) -> combWithoutTrailingDash (a, b)); assertThat (rezultat) .isEqualTo ("world-hello");

Kao što vidimo, lambda poziva našu funkciju, koja je lakša za čitanje nego stavljanje složenije implementacije u red.

3.3. Korištenje reference metode

Neki IDE-ovi automatski će nas zatražiti da gornju lambdu pretvorimo u referencu metode, jer je to često jasnije čitati.

Prepišimo naš kôd kako bismo koristili referencu metode:

Rezultat niza = Stream.of ("hello", "world") .reduce ("", this :: combWithoutTrailingDash); assertThat (rezultat) .isEqualTo ("world-hello");

Reference metoda često čine funkcionalni kôd više samorazumljivim.

4. Korištenje BiFunction

Do sada smo pokazali kako koristiti funkcije gdje su oba parametra istog tipa. The BiFunction sučelje nam omogućuje upotrebu parametara različitih vrsta, s povratnom vrijednošću treće vrste.

Zamislimo da stvaramo algoritam za kombiniranje dva popisa jednake veličine u treći popis izvođenjem operacije nad svakim parom elemenata:

Lista popisa1 = Arrays.asList ("a", "b", "c"); Popis popis2 = Arrays.asList (1, 2, 3); Rezultat popisa = novi ArrayList (); for (int i = 0; i <list1.size (); i ++) {result.add (list1.get (i) + list2.get (i)); } assertThat (result) .contensEEctly ("a1", "b2", "c3");

4.1. Generalizirajte funkciju

Ovu specijaliziranu funkciju možemo generalizirati pomoću a BiFunction kao kombinirač:

privatni statički popis listCombiner (popis list1, popis popis2, kombinacija BiFunction) {Rezultat popisa = novi ArrayList (); for (int i = 0; i <list1.size (); i ++) {result.add (combiner.apply (list1.get (i), list2.get (i))); } vratiti rezultat; }

Da vidimo što se ovdje događa. Postoje tri vrste parametara: T za vrstu stavke s prvog popisa, U za tip s drugog popisa, a zatim R za bilo koji tip funkcija kombinacije vraća.

Koristimo BiFunction omogućen ovoj funkciji pozivanjem njezine primijeniti metoda da biste dobili rezultat.

4.2. Pozivanje generalizirane funkcije

Naš kombinirač je BiFunction, koji nam omogućuje ubrizgavanje algoritma, bez obzira na vrste ulaza i izlaza. Isprobajmo:

Lista popisa1 = Arrays.asList ("a", "b", "c"); Popis popis2 = Arrays.asList (1, 2, 3); Rezultat popisa = listCombiner (list1, list2, (a, b) -> a + b); assertThat (result) .containsEhactly ("a1", "b2", "c3");

A to možemo koristiti i za potpuno različite vrste ulaza i izlaza.

Ubrizgamo algoritam da utvrdimo je li vrijednost na prvom popisu veća od vrijednosti na drugom i proizvedemo a boolean proizlaziti:

Lista popisa1 = Arrays.asList (1.0d, 2.1d, 3.3d); Lista popisa2 = Arrays.asList (0,1f, 0,2f, 4f); Rezultat popisa = listCombiner (list1, list2, (a, b) -> a> b); assertThat (result) .containsExactly (true, true, false);

4.3. A BiFunction Referenca metode

Napišimo gornji kôd izdvojenom metodom i referencom metode:

Lista popisa1 = Arrays.asList (1.0d, 2.1d, 3.3d); Lista popisa2 = Arrays.asList (0,1f, 0,2f, 4f); Rezultat popisa = listCombiner (list1, list2, this :: firstIsGreaterThanSecond); assertThat (result) .containsExactly (true, true, false); private boolean firstIsGreaterThanSecond (Double a, Float b) {return a> b; }

Treba napomenuti da ovo malo olakšava čitanje koda, kao metode firstIsGreaterThanSecond opisuje ubačeni algoritam kao referencu metode.

4.4. BiFunction Korištenje referenci metoda ovaj

Zamislimo da želimo koristiti gore navedeno BiFunction-algoritam temeljen na utvrđivanju jesu li dva popisa jednaka:

Lista popisa1 = Arrays.asList (0.1f, 0.2f, 4f); Lista popisa2 = Arrays.asList (0,1f, 0,2f, 4f); Rezultat popisa = listCombiner (list1, list2, (a, b) -> a.equals (b)); assertThat (result) .containsExactly (true, true, true);

Zapravo možemo pojednostaviti rješenje:

Rezultat popisa = listCombiner (list1, list2, Float :: jednako);

To je zato što jednako funkcioniraju u Plutati ima isti potpis kao i BiFunction. Potreban je implicitni prvi parametar ovaj, objekt tipa Plutati. Drugi parametar, drugo, tipa Objekt, je vrijednost za usporedbu.

5. Sastavljanje BiFunctions

Što ako bismo mogli upotrijebiti reference metoda da bismo učinili isto što i naš primjer usporedbe numeričkih popisa?

Lista popisa1 = Arrays.asList (1.0d, 2.1d, 3.3d); Lista popisa2 = Arrays.asList (0,1d, 0,2d, 4d); Rezultat popisa = listCombiner (list1, list2, Double :: compareTo); assertThat (result) .containsExactly (1, 1, -1);

Ovo je blisko našem primjeru, ali vraća Cijeli broj, a ne original Booleova. To je zato što usporediTo metoda u Dvostruko vraća se Cijeli broj.

Možemo dodati dodatno ponašanje koje nam je potrebno da bismo postigli svoje izvorno koristeći i onda za sastavljanje funkcije. Ovo daje a BiFunction koji prvo napravi jednu stvar s dva ulaza, a zatim izvrši drugu operaciju.

Dalje, kreirajmo funkciju koja će prisiliti referencu na našu metodu Double :: compareTo u a BiFunction:

privatna statička BiFunction asBiFunction (funkcija BiFunction) {funkcija povratka; }

Lambda ili referenca metode postaje samo BiFunction nakon što je pretvoren pozivom metode. Ovu pomoćnu funkciju možemo koristiti za pretvorbu naše lambde u BiFunction objekt eksplicitno.

Sada možemo koristiti i onda za dodavanje ponašanja povrh prve funkcije:

Lista popisa1 = Arrays.asList (1.0d, 2.1d, 3.3d); Lista popisa2 = Arrays.asList (0,1d, 0,2d, 4d); Rezultat popisa = listCombiner (list1, list2, asBiFunction (Double :: compareTo) .andThen (i -> i> 0)); assertThat (result) .containsExactly (true, true, false);

6. Zaključak

U ovom smo tutorijalu istražili BiFunction i BinaryOperator u smislu osigurane biblioteke Java Streams i vlastitih prilagođenih funkcija. Gledali smo kako proći BiFunctions pomoću lambda-a i referenci metoda, i vidjeli smo kako sastaviti funkcije.

Java knjižnice pružaju samo jednoparametarska i dvoparametarska funkcionalna sučelja. Za situacije koje zahtijevaju više parametara, pogledajte naš članak o curryingu za više ideja.

Kao i uvijek, cjeloviti uzorci koda dostupni su na GitHubu.