Útmutató a Java BiFunction Interface-hez

1. Bemutatkozás

A Java 8 bevezette a funkcionális stílusú programozást, amely lehetővé teszi számunkra, hogy az általános célú módszereket a függvények átadásával paraméterezzük.

Valószínűleg leginkább az egyparaméteres Java 8 funkcionális interfészeket ismerjük, mint pl Funkció, Állítmány, és Fogyasztó.

Ebben az oktatóanyagban olyan funkcionális interfészeket fogunk megvizsgálni, amelyek két paramétert használnak. Az ilyen függvényeket bináris függvényeknek nevezzük, és a Java-ban a BiFunction funkcionális interfész.

2. Egyparaméteres funkciók

Gyorsan összegezzük, hogyan használjuk az egyparaméteres vagy az unáris függvényt, akárcsak a folyamokban:

Lista leképezve = Stream.of ("hello", "world") .map (word -> word + "!") .Collect (Collectors.toList ()); assertThat (leképezve) .conactExactly ("hello!", "world!");

Mint láthatjuk, a térkép használ Funkció, amely egyetlen paramétert vesz fel, és lehetővé teszi számunkra, hogy műveletet hajtsunk végre ezen az értéken, új értéket adva vissza.

3. Kétparaméteres műveletek

A Java Stream könyvtár a csökkenteni függvény, amely lehetővé teszi számunkra, hogy összekapcsoljuk a folyam elemeit. Ki kell fejeznünk, hogyan alakítják át az eddig felhalmozott értékeket a következő elem hozzáadásával.

A csökkenteni A function a funkcionális interfészt használja BinaryOperator, amely két azonos típusú objektumot vesz fel a bemeneteként.

Képzeljük el, hogy a streamünk összes eleméhez szeretnénk csatlakozni úgy, hogy az újakat egy kötőjel-elválasztóval elöl helyezzük el. A következő szakaszokban áttekintünk néhány megvalósítási módot.

3.1. Lambda használata

A lambda megvalósítása a BiFunction előtagja két paraméter, zárójelekkel körülvéve:

Karakterlánc eredménye = Stream.of ("hello", "world") .reduce ("", (a, b) -> b + "-" + a); assertThat (eredmény) .isEqualTo ("hello-helló");

Mint láthatjuk, a két érték, a és b vannak Húrok. Írtunk egy lambdát, amely ötvözi őket a kívánt kimenet elkészítéséhez, először a másodikkal, és közöttük egy kötőjellel.

Meg kell jegyeznünk csökkenteni kezdőértéket használ - ebben az esetben az üres karakterláncot. Így a végén egy zárójel kerül a fenti kóddal, mivel a streamünk első értéke hozzá van kötve.

Azt is meg kell jegyeznünk, hogy a Java típusú következtetés lehetővé teszi számunkra, hogy a paramétereink típusait legtöbbször kihagyjuk. Olyan helyzetekben, amikor a lambda típusa nem világos a kontextusból, típusokat használhatunk a paramétereinkhez:

Karakterlánc eredménye = Stream.of ("hello", "world") .reduce ("", (a karakterlánc, b karakterlánc) -> b + "-" + a);

3.2. Funkció használata

Mi lenne, ha azt akarnánk elérni, hogy a fenti algoritmus ne legyen kötőjel a végén? Írhatnánk több kódot a lambdánkba, de ez rendetlenné válhat. Cseréljünk ki inkább egy függvényt:

private String combWithoutTrailingDash (a karakterlánc, b karakterlánc) {if (a.isEmpty ()) {return b; } return b + "-" + a; }

És hívd:

Karakterlánc eredménye = Stream.of ("hello", "world") .reduce ("", (a, b) -> combWithoutTrailingDash (a, b)); assertThat (eredmény) .isEqualTo ("hello world");

Mint láthatjuk, a lambda hívja a függvényünket, amely könnyebben olvasható, mint a bonyolultabb megvalósítás beillesztése.

3.3. A Method Reference használata

Egyes IDE-k automatikusan arra késztetnek bennünket, hogy a fenti lambdát módszer referenciává alakítsuk át, mivel az olvasás gyakran világosabb.

Írjuk át kódunkat metódus referencia használatára:

Karakterlánc eredménye = Stream.of ("szia", ​​"világ") .reduce ("", ez :: combWithoutTrailingDash); assertThat (eredmény) .isEqualTo ("helló világ");

A módszerre való hivatkozások gyakran a funkciókódot jobban megmagyarázzák.

4. Használata BiFunction

Eddig bemutattuk, hogyan lehet olyan függvényeket használni, ahol mindkét paraméter azonos típusú. A BiFunction felület lehetővé teszi számunkra, hogy különböző típusú paramétereket használjunk, egy harmadik típusú visszatérési értékkel.

Képzeljük el, hogy algoritmust hozunk létre, amely két azonos méretű listát egyesít egy harmadik listává egy művelet végrehajtásával minden elempáron:

List list1 = Tömbök.asList ("a", "b", "c"); List list2 = Tömbök.asList (1, 2, 3); Lista eredménye = new ArrayList (); for (int i = 0; i <list1.size (); i ++) {result.add (list1.get (i) + list2.get (i)); } assertThat (eredmény) .containsExactly ("a1", "b2", "c3");

4.1. Általánosítsa a funkciót

Ezt a speciális funkciót általánosíthatjuk az a használatával BiFunction mint kombinátor:

privát statikus List listCombiner (List list1, List list2, BiFunction combiner) {List result = new ArrayList (); for (int i = 0; i <list1.size (); i ++) {result.add (combiner.apply (list1.get (i), list2.get (i))); } visszatérési eredmény; }

Nézzük, mi folyik itt. Háromféle paraméter létezik: T az első listában szereplő elem típusa, U a második listában szereplő típushoz, majd R bármilyen típusú típus esetén a kombinációs függvény visszatér.

Használjuk a BiFunction ennek a funkciónak a megadásával biztosítja alkalmaz módszer hogy megkapja az eredményt.

4.2. Az Általános funkció meghívása

Kombinátorunk a BiFunction, amely lehetővé teszi számunkra, hogy beadjunk egy algoritmust, függetlenül a bemenet és a kimenet típusától. Próbáljuk ki:

List list1 = Tömbök.asList ("a", "b", "c"); List list2 = Tömbök.asList (1, 2, 3); Lista eredménye = listCombiner (lista1, lista2, (a, b) -> a + b); assertThat (eredmény) .containsExactly ("a1", "b2", "c3");

És ezt teljesen más típusú bemenetekre és kimenetekre is felhasználhatjuk.

Injektáljunk egy algoritmust annak megállapítására, hogy az első lista értéke nagyobb-e, mint a második értéke, és állítsuk elő a logikai eredmény:

List list1 = Tömbök.asList (1.0d, 2.1d, 3.3d); List list2 = Tömbök.asList (0,1f, 0,2f, 4f); Lista eredménye = listCombiner (lista1, lista2, (a, b) -> a> b); assertThat (eredmény) .conactExactly (igaz, igaz, hamis);

4.3. A BiFunction Módszer referencia

Írjuk át a fenti kódot kibontott módszerrel és metódus-referenciával:

List list1 = Tömbök.asList (1.0d, 2.1d, 3.3d); List list2 = Tömbök.asList (0,1f, 0,2f, 4f); Lista eredménye = listCombiner (lista1, lista2, ez :: firstIsGreaterThanSecond); assertThat (eredmény) .conactExactly (igaz, igaz, hamis); private boolean firstIsGreaterThanSecond (Dupla a, Float b) {return a> b; }

Meg kell jegyeznünk, hogy ez egy kicsit megkönnyíti a kód olvasását, mint a módszer firstIsGreaterThanSecond leírja a módszer referenciaként beadott algoritmust.

4.4. BiFunction Módszer hivatkozások ez

Képzeljük el, hogy a fentieket szeretnénk használni BiFunction-alapú algoritmus annak meghatározására, hogy két lista egyenlő-e:

List list1 = tömbök. AsList (0,1f, 0,2f, 4f); List list2 = Tömbök.asList (0,1f, 0,2f, 4f); Lista eredménye = listCombiner (lista1, lista2, (a, b) -> a.egyenlő (b)); assertThat (eredmény) .conactExactly (igaz, igaz, igaz);

Valójában leegyszerűsíthetjük a megoldást:

Lista eredménye = listCombiner (list1, list2, Float :: egyenlő);

Ez azért van, mert a egyenlő funkció be Úszó aláírása megegyezik a BiFunction. A implicit első paraméterére van szükség ez, típusú objektum Úszó. A második paraméter, Egyébtípusú Tárgy, az összehasonlítandó érték.

5. Komponálás BiFunkciók

Mi lenne, ha a metódus-hivatkozásokkal ugyanazt tehetnénk, mint a numerikus listák összehasonlítási példája?

List list1 = Tömbök.asList (1.0d, 2.1d, 3.3d); List list2 = Tömbök.asList (0,1d, 0,2d, 4d); Lista eredménye = listCombiner (list1, list2, Double :: CompareTo); assertThat (eredmény). pontosan tartalmaz (1, 1, -1);

Ez közel áll a példánkhoz, de egy Egész szám, nem pedig az eredeti Logikai. Ez azért van, mert a összehasonlítani módszer ben Kettős visszatér Egész szám.

Hozzáadhatjuk azt az extra viselkedést, amelyre szükségünk van az eredeti eléréséhez felhasználásával és akkor függvény összeállításához. Ez előállítja a BiFunction hogy először a két bemenettel csinál egy dolgot, majd elvégez egy másik műveletet.

Ezután hozzunk létre egy függvényt a metódus referenciánk kényszerítésére Dupla :: hasonlít ba be BiFunction:

privát statikus BiFunction asBiFunction (BiFunction function) {return function; }

A lambda vagy módszer referencia csak a-vá válik BiFunction miután metódushívással konvertálta. Ezt a segítő funkciót használhatjuk lambdánk konvertálására BiFunction objektum kifejezetten.

Most már használhatjuk és akkor viselkedés hozzáadásához az első függvény tetején:

List list1 = Tömbök.asList (1.0d, 2.1d, 3.3d); List list2 = Tömbök.asList (0,1d, 0,2d, 4d); Lista eredménye = listCombiner (list1, list2, asBiFunction (Double :: CompareTo) .andThen (i -> i> 0)); assertThat (eredmény) .conactExactly (igaz, igaz, hamis);

6. Következtetés

Ebben az oktatóanyagban feltártuk BiFunction és BinaryOperator a biztosított Java Streams könyvtár és a saját egyedi funkcióink szempontjából. Megnéztük, hogyan tovább BiFunkciók lambdas és metódus referenciák felhasználásával, és láttuk, hogyan kell komponálni a függvényeket.

A Java könyvtárak csak egy- és kétparaméteres funkcionális interfészeket biztosítanak. További paramétereket igénylő helyzetekért további ötleteket a curryezésről szóló cikkünkben talál.

Mint mindig, a teljes kódminták elérhetők a GitHubon.