Veiledning til Java BiFunction Interface

1. Introduksjon

Java 8 introduserte funksjonell stilprogrammering, slik at vi kunne parametere metoder for generell bruk ved å gi funksjoner.

Vi er sannsynligvis mest kjent med Java 8-funksjonelle grensesnitt som en parameter Funksjon, Predikere, og Forbruker.

I denne opplæringen skal vi se på funksjonelle grensesnitt som bruker to parametere. Slike funksjoner kalles binære funksjoner og er representert i Java med BiFunction funksjonelt grensesnitt.

2. Enkeltparameterfunksjoner

La oss raskt oppsummere hvordan vi bruker en enkeltparameter eller unary-funksjon, slik vi gjør i strømmer:

Liste kartlagt = Stream.of ("hei", "verden"). Kart (ord -> ord + "!") .Collect (Collectors.toList ()); assertThat (kartlagt) .containsExactly ("hei!", "verden!");

Som vi kan se, er kart bruker Funksjon, som tar en enkelt parameter og lar oss utføre en operasjon på den verdien, og returnere en ny verdi.

3. Operasjoner med to parametere

Java Stream-biblioteket gir oss en redusere funksjon som lar oss kombinere elementene i en strøm. Vi må uttrykke hvordan verdiene vi har akkumulert så langt transformeres ved å legge til neste element.

De redusere funksjonen bruker det funksjonelle grensesnittet BinaryOperator, som tar to objekter av samme type som inngangene.

La oss forestille oss at vi vil bli med på alle elementene i strømmen vår ved å sette de nye foran med en bindestrek-skilletegn. Vi vil se på noen måter å implementere dette på i de følgende avsnittene.

3.1. Bruke en Lambda

Implementeringen av en lambda for en BiFunction er prefikset av to parametere, omgitt av parentes:

String result = Stream.of ("hallo", "world") .reduce ("", (a, b) -> b + "-" + a); assertThat (result) .isEqualTo ("world-hallo-");

Som vi kan se, er de to verdiene, en og b er Strenger. Vi har skrevet en lambda som kombinerer dem for å oppnå ønsket utgang, med den andre først, og en dash innimellom.

Vi bør merke oss det redusere bruker en startverdi - i dette tilfellet den tomme strengen. Dermed ender vi med et etterfølgende dash med ovennevnte kode, da den første verdien fra strømmen vår blir forbundet med den.

Vi bør også være oppmerksom på at Java's type inferens lar oss utelate typene av parametrene våre mesteparten av tiden. I situasjoner der typen lambda ikke er tydelig fra sammenhengen, kan vi bruke typer for parametrene våre:

String result = Stream.of ("hallo", "world") .reduce ("", (String a, String b) -> b + "-" + a);

3.2. Bruke en funksjon

Hva om vi ønsket å gjøre at algoritmen ovenfor ikke satte en strek på slutten? Vi kan skrive mer kode i lambdaen vår, men det kan bli rotete. La oss trekke ut en funksjon i stedet:

privat streng combineWithoutTrailingDash (streng a, streng b) {hvis (a.isEmpty ()) {retur b; } returner b + "-" + a; }

Og så kall det:

String result = Stream.of ("hallo", "world") .reduce ("", (a, b) -> combineWithoutTrailingDash (a, b)); assertThat (resultat) .isEqualTo ("verden-hei");

Som vi kan se, kaller lambda vår funksjon, som er lettere å lese enn å sette den mer komplekse implementeringen på plass.

3.3. Ved hjelp av en metodereferanse

Noen IDEer vil automatisk be oss om å konvertere lambda ovenfor til en metodereferanse, da det ofte er tydeligere å lese.

La oss omskrive koden vår for å bruke en metodereferanse:

String result = Stream.of ("hallo", "world") .reduce ("", this :: combineWithoutTrailingDash); assertThat (resultat) .isEqualTo ("verden-hei");

Metodehenvisninger gjør ofte funksjonskoden mer selvforklarende.

4. Bruke BiFunction

Så langt har vi demonstrert hvordan du bruker funksjoner der begge parametrene er av samme type. De BiFunction grensesnitt lar oss bruke parametere av forskjellige typer, med en returverdi av en tredje type.

La oss forestille oss at vi lager en algoritme for å kombinere to lister av samme størrelse til en tredje liste ved å utføre en operasjon på hvert elementpar:

Liste liste1 = Arrays.asList ("a", "b", "c"); List list2 = Arrays.asList (1, 2, 3); Listeresultat = ny ArrayList (); for (int i = 0; i <list1.size (); i ++) {result.add (list1.get (i) + list2.get (i)); } assertThat (result) .containsExactly ("a1", "b2", "c3");

4.1. Generaliser funksjonen

Vi kan generalisere denne spesialiserte funksjonen ved hjelp av a BiFunction som kombinatoren:

privat statisk 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))); } returnere resultat; }

La oss se hva som skjer her. Det er tre typer parametere: T for varetypen i den første listen, U for typen i den andre listen, og deretter R for hvilken type kombinasjonsfunksjonen returnerer.

Vi bruker BiFunction gitt til denne funksjonen ved å ringe dens søke om metode for å få resultatet.

4.2. Ringe til den generelle funksjonen

Vår kombinator er en BiFunction, som lar oss injisere en algoritme, uansett hvilken type inngang og utgang. La oss prøve det:

Liste liste1 = Arrays.asList ("a", "b", "c"); List list2 = Arrays.asList (1, 2, 3); Listresultat = listCombiner (liste1, liste2, (a, b) -> a + b); assertThat (resultat) .containsExactly ("a1", "b2", "c3");

Og vi kan bruke dette til helt forskjellige typer innganger og utganger også.

La oss injisere en algoritme for å bestemme om verdien i den første listen er større enn verdien i den andre og produsere en boolsk resultat:

Liste liste1 = Arrays.asList (1.0d, 2.1d, 3.3d); Liste liste2 = Arrays.asList (0.1f, 0.2f, 4f); Listeresultat = listCombiner (liste1, liste2, (a, b) -> a> b); assertThat (resultat) .containsExactly (true, true, false);

4.3. EN BiFunction Metodehenvisning

La oss omskrive ovennevnte kode med en utvunnet metode og en metodereferanse:

Liste liste1 = Arrays.asList (1.0d, 2.1d, 3.3d); Liste liste2 = Arrays.asList (0.1f, 0.2f, 4f); Listresultat = listCombiner (liste1, liste2, dette :: firstIsGreaterThanSecond); assertThat (result) .containsExactly (true, true, false); privat boolsk firstIsGreaterThanSecond (Double a, Float b) {return a> b; }

Vi bør merke oss at dette gjør koden litt lettere å lese, som metoden firstIsGreaterThanSecond beskriver algoritmen som er injisert som en metodereferanse.

4.4. BiFunction Metode Referanser Bruke dette

La oss forestille oss at vi vil bruke ovennevnte BiFunction-basert algoritme for å bestemme om to lister er like:

Liste liste1 = Arrays.asList (0.1f, 0.2f, 4f); Liste liste2 = Arrays.asList (0.1f, 0.2f, 4f); Listeresultat = listCombiner (liste1, liste2, (a, b) -> a. Likestilling (b)); assertThat (result) .containsExactly (true, true, true);

Vi kan faktisk forenkle løsningen:

Listresultat = listCombiner (liste1, liste2, flyte :: er lik);

Dette er fordi er lik funksjon i Flyte har samme signatur som en BiFunction. Det tar en implisitt første parameter for dette, et objekt av typen Flyte. Den andre parameteren, annen, av typen Gjenstand, er verdien å sammenligne.

5. Å komponere BiFunksjoner

Hva om vi kunne bruke metodereferanser til å gjøre det samme som vårt eksempel på sammenligning av numeriske lister?

Liste liste1 = Arrays.asList (1.0d, 2.1d, 3.3d); Liste liste2 = Arrays.asList (0.1d, 0.2d, 4d); Listresultat = listCombiner (liste1, liste2, Dobbelt :: sammenlignTo); assertThat (resultat). inneholder nøyaktig (1, 1, -1);

Dette er nær vårt eksempel, men returnerer et Heltall, snarere enn originalen Boolsk. Dette er fordi sammenligne med metode i Dobbelt returnerer Heltall.

Vi kan legge til den ekstra atferden vi trenger for å oppnå originalen vår ved hjelp av og så å komponere en funksjon. Dette gir en BiFunction som først gjør en ting med de to inngangene og deretter utfører en annen operasjon.

La oss deretter lage en funksjon for å tvinge metodehenvisningen vår Dobbelt :: sammenlignTo inn i en BiFunction:

privat statisk BiFunction asBiFunction (BiFunction-funksjon) {returfunksjon; }

En lambda- eller metodereferanse blir bare en BiFunction etter at den er konvertert ved en metodeinnkallelse. Vi kan bruke denne hjelpefunksjonen til å konvertere lambdaen vår til BiFunction objekt eksplisitt.

Nå kan vi bruke og så for å legge til atferd på toppen av den første funksjonen:

Liste liste1 = Arrays.asList (1.0d, 2.1d, 3.3d); Liste liste2 = Arrays.asList (0.1d, 0.2d, 4d); Listresultat = listCombiner (liste1, liste2, asBiFunction (Dobbelt :: sammenlignTo) .ogDå (i -> i> 0)); assertThat (result) .containsExactly (true, true, false);

6. Konklusjon

I denne veiledningen har vi utforsket BiFunction og BinaryOperator når det gjelder det medfølgende Java Streams-biblioteket og våre egne tilpassede funksjoner. Vi har sett på hvordan vi skal passere BiFunksjoner ved hjelp av lambdas og metodereferanser, og vi har sett hvordan vi kan komponere funksjoner.

Java-bibliotekene tilbyr bare en- og to-parameter funksjonelle grensesnitt. For situasjoner som krever flere parametere, se vår artikkel om karri for flere ideer.

Som alltid er de komplette kodeeksemplene tilgjengelig på GitHub.


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