Programavimas

Funkcinis programavimas „Java“ kūrėjams, 2 dalis

Sveiki sugrįžę į šią dviejų dalių pamoką, kurioje pristatomas funkcinis programavimas „Java“ kontekste. „Java“ kūrėjų funkcinio programavimo dalyje, 1 dalyje, aš naudoju „JavaScript“ pavyzdžius, kad galėtumėte pradėti naudoti penkias funkcines programavimo technikas: grynosios funkcijos, aukštesnės eilės funkcijos, tingus vertinimas, uždarymai ir karijavimas. Šių pavyzdžių pateikimas „JavaScript“ leido sutelkti dėmesį į metodus paprastesne sintakse, neįsigilinant į sudėtingesnes „Java“ funkcines programavimo galimybes.

2 dalyje mes dar kartą apžvelgsime tuos metodus naudodami „Java“ kodą, kuris buvo ankstesnis nei „Java 8“. Kaip pamatysite, šis kodas yra funkcionalus, tačiau jį rašyti ar skaityti nėra lengva. Jūs taip pat supažindinsite su naujomis funkcinėmis programavimo funkcijomis, kurios buvo visiškai integruotos į „Java“ kalbą „Java 8“; būtent lambdas, metodų nuorodas, funkcines sąsajas ir srautų API.

Šioje pamokoje apžvelgsime 1 dalies pavyzdžius, kad pamatytume, kaip lyginami „JavaScript“ ir „Java“ pavyzdžiai. Taip pat pamatysite, kas nutiks, kai atnaujinsiu kai kuriuos ankstesnius „Java 8“ pavyzdžius su funkcinėmis kalbos funkcijomis, tokiomis kaip „lambdas“ ir metodo nuorodomis. Galiausiai, šioje pamokoje yra praktinis pratimas, sukurtas jums padėti praktikuokite funkcinį mąstymą, kurį atliksite transformuodami objektyviai nukreipto „Java“ kodo fragmentą į jo funkcinį atitikmenį.

atsisiųsti Gauti kodą Atsisiųskite šaltinį, pvz., programas, šioje pamokoje. Sukūrė Jeffas Friesenas, skirtas „JavaWorld“.

Funkcinis programavimas su Java

Daugelis kūrėjų to nesuvokia, tačiau prieš Java 8 buvo galima parašyti funkcines programas „Java“. Norėdami turėti išsamų „Java“ funkcinio programavimo vaizdą, greitai peržiūrėkime funkcines programavimo funkcijas, kurios buvo ankstesnės nei „Java 8“. Tai sumažino, tikriausiai labiau įvertinsite, kaip naujos „Java 8“ funkcijos (pvz., „lambdas“ ir funkcinės sąsajos) supaprastino „Java“ požiūrį į funkcinį programavimą.

„Java“ palaikymo funkciniam programavimui ribos

Net patobulinus funkcinius programavimo „Java 8“ variantus, „Java“ išlieka būtina, į objektus orientuota programavimo kalba. Trūksta diapazono tipų ir kitų funkcijų, dėl kurių jis būtų funkcionalesnis. „Java“ taip pat trukdo įvardijimas rašyti, o tai yra nuostata, kad kiekvienas tipas turi turėti pavadinimą. Nepaisant šių apribojimų, kūrėjams, kurie naudojasi „Java“ funkcinėmis funkcijomis, vis tiek naudinga tai, kad jie gali parašyti glaustesnį, daugkartinio naudojimo ir įskaitomą kodą.

Funkcinis programavimas prieš „Java 8“

Anoniminės vidinės klasės kartu su sąsajomis ir uždarymu yra trys senesnės funkcijos, palaikančios funkcinį programavimą senesnėse „Java“ versijose:

  • Anoniminės vidinės klasės Leiskite jums perduoti funkcionalumą (aprašytą sąsajomis) metodams.
  • Funkcinės sąsajos yra sąsajos, apibūdinančios funkciją.
  • Uždarymai leidžia jums pasiekti kintamuosius jų išorinėse srityse.

Tolesniuose skyriuose apžvelgsime penkis 1 dalyje pateiktus metodus, tačiau naudodami „Java“ sintaksę. Pamatysite, kaip visi šie funkciniai metodai buvo įmanomi prieš pradedant „Java 8“.

Rašyti grynas funkcijas Java

1 sąraše pateikiamas programos pavyzdžio šaltinio kodas, DienosMėnesis, kuris yra parašytas naudojant anoniminę vidinę klasę ir funkcinę sąsają. Ši programa parodo, kaip parašyti gryną funkciją, kurią „Java“ buvo galima pasiekti dar prieš „Java 8“.

Sąrašas 1. Gryna „Java“ funkcija (DaysInMonth.java)

sąsaja Funkcija {R taikyti (T t); } public class DaysInMonth {public static void main (String [] args) {Function dim = new Function () {@Paisyti viešąjį sveikąjį skaičių (mėnuo sveikas) {grąžinti naują sveiką skaičių [] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} [mėnuo]; }}; System.out.printf ("balandis:% d% n", dim.apply (3)); System.out.printf ("rugpjūtis:% d% n", dim.apply (7)); }}

Bendrasis Funkcija sąsaja 1 sąraše apibūdina funkciją su vienu tipo parametru T ir grąžinimo tipo tipas R. Funkcija sąsaja deklaruoja R taikoma (T t) metodas, taikantis šią funkciją nurodytam argumentui.

pagrindinis () metodas sukuria anoniminę vidinę klasę, kuri įgyvendina Funkcija sąsaja. kreiptis () metodas ištrina dėžutes mėnesį ir naudoja jį indeksuodamas dienų skaičių per mėnesį skaičių masyvą. Grąžinamas šio indekso sveikasis skaičius. (Aš paprastumo dėlei ignoruoju kelerius metus.)

pagrindinis () Kitas šią funkciją vykdo du kartus, pasitelkdamas kreiptis () grąžinti balandžio ir rugpjūčio dienų skaičiavimus. Šie skaičiai vėliau atspausdinami.

Mums pavyko sukurti funkciją ir gryną funkciją! Prisiminkime, kad a grynoji funkcija priklauso tik nuo jo argumentų ir jokios išorinės būsenos. Šalutinio poveikio nėra.

Sudarykite 1 sąrašą taip:

javac DaysInMonth.java

Paleiskite gautą programą taip:

java „DaysInMonth“

Turėtumėte stebėti šį rezultatą:

Balandis: rugpjūčio 30: 31

Aukštesnės eilės funkcijų rašymas „Java“

Toliau panagrinėsime aukštesnės eilės funkcijas, dar vadinamas pirmos klasės funkcijomis. Atminkite, kad a aukštesnės eilės funkcija gauna funkcijos argumentus ir (arba) grąžina funkcijos rezultatą. „Java“ susieja funkciją su metodu, kuris apibrėžiamas anoniminėje vidinėje klasėje. Šios klasės egzempliorius perduodamas kitam „Java“ metodui, kuris naudojamas kaip aukštesnės eilės funkcija, arba grąžinamas iš jo. Šis į failą orientuotas kodo fragmentas rodo funkcijos perdavimą aukštesnės eilės funkcijai:

File [] txtFiles = new File ("."). ListFiles (new FileFilter () {@Paisyti viešąjį loginį priėmimą (Failo kelio pavadinimas) {return pathname.getAbsolutePath (). EndWith ("txt");}});

Šis kodo fragmentas perduoda funkciją, pagrįstą java.io.FileFilter funkcinė sąsaja su java.io.Failas klasės Failas [] listFiles („FileFilter“ filtras) metodas, liepdamas grąžinti tik tuos failus su txt pratęsimai.

2 sąraše rodomas kitas būdas dirbti su aukštesnės eilės „Java“ funkcijomis. Tokiu atveju kodas perduoda lyginamąją funkciją a rūšiuoti () aukštesnės eilės funkcija rūšiavimui didėjančia tvarka ir antroji lyginamoji funkcija rūšiuoti () rūšiavimui mažėjančia tvarka.

Aukštesnės eilės „Java“ funkcija (Sort.java)

importuoti java.util.Comparator; viešoji klasė Rūšiuoti {public static void main (String [] args) {String [] internalplanets = {"Merkurijus", "Venera", "Žemė", "Marsas"}; sąvartynas (vidinės planetos); rūšiuoti (vidinės planetos, naujas lygintuvas () {@Paisyti viešąjį int palyginimą (eilutė e1, eilutė e2) {grąžinti e1.compareTo (e2);}}); sąvartynas (vidinės planetos); rūšiuoti (vidinės planetos, naujas „Comparator“) {@Paisyti viešąjį int palyginimą (eilutė e1, eilutė e2) {grąžinti e2.compareTo (e1);}}); sąvartynas (vidinės planetos); } static void dump (T [] masyvas) {for (T elementas: masyvas) System.out.println (elementas); System.out.println (); } static void sort (T [] masyvas, komparatoriaus cmp) {for (int pass = 0; pass  praeiti; i--) if (cmp.compare (masyvas [i], masyvas [perduoti]) <0) apsikeitimas (masyvas, i, perdavimas); } static void swap (T [] masyvas, int i, int j) {T temp = masyvas [i]; masyvas [i] = masyvas [j]; masyvas [j] = temp; }}

2 sąraše importuojama java.util.Palygintojas funkcinė sąsaja, kuri apibūdina funkciją, kuria galima palyginti du savavališko, bet identiško tipo objektus.

Dvi reikšmingos šio kodo dalys yra rūšiuoti () metodas (įgyvendinantis „Bubble Sort“ algoritmą) ir rūšiuoti () invokacijos į pagrindinis () metodas. Nors rūšiuoti () toli gražu nėra funkcionalus, jis parodo aukštesnės eilės funkciją, kuri funkciją - lyginamąjį - gauna kaip argumentą. Šią funkciją ji vykdo, remdamasi ja palyginti () metodas. Du šios funkcijos atvejai perduodami dviem rūšiuoti () skambina pagrindinis ().

Sudarykite 2 sąrašą taip:

javac Rūšiuoti.java

Paleiskite gautą programą taip:

java Rūšiuoti

Turėtumėte stebėti šį rezultatą:

Merkurijaus Venera Žemė Marsas Žemė Marsas Merkurijus Venera Venera Gyvsidabris Marsas Žemė

Tingi vertinimas Java kalba

Tingi vertinimas yra dar viena funkcinė programavimo technika, kuri „Java 8“ nėra naujiena. Ši technika atideda išraiškos vertinimą, kol prireiks jos vertės. Daugeliu atvejų „Java“ noriai vertina išraišką, susietą su kintamuoju. „Java“ palaiko tingų šios sintaksės vertinimą:

  • Būlo && ir || operatoriai, kurie nevertins savo dešiniojo operando, kai kairysis operandas yra melagingas (&&) arba tiesa (||).
  • ?: operatorius, kuris įvertina Būlio išraišką ir vėliau įvertina tik vieną iš dviejų alternatyvių išraiškų (suderinamo tipo), remdamasis Būlo išraiškos teisinga / klaidinga verte.

Funkcinis programavimas skatina į išraišką orientuotą programavimą, todėl norėsite kiek įmanoma vengti teiginių. Pvz., Tarkime, kad norite pakeisti „Java“ jei-Kitas pareiškimas su an ifThenElse () metodas. 3 sąraše parodytas pirmasis bandymas.

3 sąrašas. Nekantraus „Java“ vertinimo pavyzdys (EagerEval.java)

public class EagerEval {public static void main (String [] args) {System.out.printf ("% d% n", ifThenElse (true, kvadratas (4), kubas (4))); System.out.printf ("% d% n", ifThenElse (klaidingas, kvadratas (4), kubas (4))); } statinis int kubas (int x) {System.out.println ("kube"); grįžti x * x * x; } static int ifThenElse (loginis predikatas, int onTrue, int onFalse) {return (predikatas)? onTrue: onFalse; } statinis int kvadratas (int x) {System.out.println ("kvadrate"); grįžti x * x; }}

3 sąrašas apibrėžia ifThenElse () metodas, reikalaujantis Būlio predikato ir sveikųjų skaičių poros, grąžinant tiesa sveikasis skaičius, kai predikatas yra tiesa ir onFalse sveikasis skaičius kitaip.

3 sąrašas taip pat apibrėžia kubas() ir kvadratas () metodai. Atitinkamai, šie metodai kubą ir kvadratą nurodo sveikąjį skaičių ir pateikia rezultatą.

pagrindinis () metodas remiasi ifThenElse (tiesa, kvadratas (4), kubas (4)), kuri turėtų remtis tik kvadratas (4), po kurio seka ifThenElse (klaidingas, kvadratas (4), kubas (4)), kuri turėtų remtis tik kubas (4).

Sudarykite 3 sąrašą taip:

javac EagerEval.java

Paleiskite gautą programą taip:

java EagerEval

Turėtumėte stebėti šį rezultatą:

16 kvadrato kvadratas 64 kubo kvadratas

Išvestis rodo, kad kiekvienas ifThenElse () skambinkite abiejų metodų vykdymo rezultatais, nepriklausomai nuo loginės išraiškos. Mes negalime panaudoti ?: operatoriaus tingumas, nes „Java“ noriai vertina metodo argumentus.

Nors jokiu būdu negalima išvengti noro įvertinti metodo argumentus, vis tiek galime tuo pasinaudoti ?:tingus vertinimas, siekiant užtikrinti, kad tik kvadratas () arba kubas() vadinamas. 4 sąrašas rodo, kaip.

4. sąrašas tinginio vertinimo „Java“ pavyzdžiu (LazyEval.java)

sąsaja Funkcija {R taikyti (T t); } public class LazyEval {public static void main (String [] args) {Funkcijos kvadratas = new Funkcija () {{System.out.println ("SQUARE"); } @Paisyti viešąjį sveikąjį skaičių (sveikasis skaičius t) {System.out.println ("kvadratu"); grįžti t * t; }}; Funkcijos kubas = nauja funkcija () {{System.out.println ("CUBE"); } @Paisyti viešąjį sveikąjį skaičių (sveikasis skaičius t) {System.out.println ("kube"); grįžti t * t * t; }}; System.out.printf ("% d% n", ifThenElse (tiesa, kvadratas, kubas, 4)); System.out.printf ("% d% n", ifThenElse (klaidingas, kvadratas, kubas, 4)); } statinis R ifThenElse (loginis predikatas, Funkcija onTrue, Funkcija onFalse, T t) {return (predikatas? onTrue.apply (t): onFalse.apply (t)); }}

Išvardijami 4 posūkiai ifThenElse () į aukštesnės eilės funkciją, deklaruojant šį metodą gauti porą Funkcija argumentai. Nors šie argumentai yra nekantriai vertinami, kai perduodami ifThenElse (), ?: operatorius priverčia vykdyti tik vieną iš šių funkcijų (per kreiptis ()). Renkant ir vykdant programą galite pamatyti norų ir tingų vertinimą darbe.

Sudarykite 4 sąrašą taip:

javac LazyEval.java

Paleiskite gautą programą taip:

java LazyEval

Turėtumėte stebėti šį rezultatą:

Kvadrato kvadratas 16 kvadrate 64 kube

Tingi iteratoriai ir dar daugiau

Nealo Fordo „Tingumas, 1 dalis: tingaus vertinimo tyrinėjimas„ Java ““ suteikia daugiau įžvalgos apie tingų vertinimą. Autorius pateikia „Java“ pagrindu veikiantį tinginių iteratorių kartu su pora tingiai orientuotų „Java“ sistemų.

Uždarymai „Java“

Anoniminis vidinės klasės egzempliorius siejamas su a uždarymas. Turi būti deklaruojami išorinės apimties kintamieji galutinis arba (pradedant „Java 8“) faktiškai galutinis (turint omenyje, kad po inicijavimo yra nemodifikuotas), kad būtų prieinama. Apsvarstykite 5 sąrašą.

5 sąrašas. „Java“ uždarymo pavyzdys (PartialAdd.java)

sąsaja Funkcija {R taikyti (T t); } public class PartialAdd {Funkcijos pridėjimas (galutinis int x) {Funkcijos dalinis pridėjimas = nauja funkcija () {@Paisyti viešąjį sveikąjį skaičių (Integer y) {return y + x; }}; grąžinti dalinįPridėti; } public static void main (String [] args) {PartialAdd pa = new PartialAdd (); Funkcija add10 = pa.add (10); Funkcija add20 = pa.add (20); System.out.println (add10.apply (5)); System.out.println (add20.apply (5)); }}

5 sąrašas yra „Java“ atitikmuo, kurį anksčiau pateikiau „JavaScript“ (žr. 1 dalies 8 sąrašą). Šis kodas deklaruoja papildyti() aukštesnės eilės funkcija, kuri grąžina funkciją, skirtą atlikti dalinį papildyti() funkcija. kreiptis () metodas pasiekia kintamąjį x išorinėje papildyti(), kuris turi būti deklaruotas galutinis prieš „Java 8.“. Kodas elgiasi beveik taip pat, kaip ir „JavaScript“ atitikmuo.

Sudarykite 5 sąrašą taip:

javac PartialAdd.java

Paleiskite gautą programą taip:

java PartialAdd

Turėtumėte stebėti šį rezultatą:

15 25

Karis „Java“

Galbūt pastebėjote, kad PartialAdd 5 sąraše rodo ne tik uždarymą. Tai taip pat demonstruoja karis, kuris yra būdas kelių argumentų funkcijos vertimą paversti lygiaverčio vieno argumento funkcijų sekos vertinimu. Tiek pr. pridėti (10) ir pr. pridėti (20) 5 sąraše pateikia operandą įrašantį uždarymą (10 arba 20, atitinkamai) ir funkcija, atliekanti papildymą - antrasis operandas (5) perduodamas per pridėti10.apply (5) arba add20.apply (5).

„Currying“ leidžia mums įvertinti funkcijos argumentus po vieną, sukuriant naują funkciją su vienu mažiau argumentų kiekviename žingsnyje. Pavyzdžiui, PartialAdd taikome šią funkciją:

f (x, y) = x + y

Mes galėtume taikyti abu argumentus tuo pačiu metu, suteikdami:

f (10, 5) = 10 + 5

Tačiau, naudodami karį, taikome tik pirmąjį argumentą, duodami tai:

f (10, y) = g (y) = 10 + y

Dabar turime vieną funkciją, g, tam reikia tik vieno argumento. Tai funkcija, kuri bus įvertinta, kai iškviesime kreiptis () metodas.

Dalinis taikymas, o ne dalinis papildymas

Pavadinimas PartialAdd reiškia dalinis taikymaspapildyti() funkcija. Tai nereiškia dalinio papildymo. „Currying“ yra susijęs su daliniu funkcijos taikymu. Tai ne apie dalinių skaičiavimų atlikimą.

Jus gali supainioti tai, kad vartoju frazę „dalinis taikymas“, ypač todėl, kad 1 dalyje nurodžiau, kad karis nėra tas pats, kas dalinis taikymas, kuris yra daugybės argumentų pritvirtinimas prie funkcijos, sukuriant kitą mažesnio ariteto funkciją. Naudodamiesi daline programa, galite sukurti funkcijas su daugiau nei vienu argumentu, tačiau naudojant curry, kiekviena funkcija turi turėti tiksliai vieną argumentą.

5 sąraše pateikiamas nedidelis „Java“ pagrindu sukurto karijavimo pavyzdys prieš „Java 8“. Dabar apsvarstykite CurriedCalc paraiška 6 sąraše.

6 sąrašas. „Currying“ „Java“ kode („CurriedCalc.java“)

sąsaja Funkcija {R taikyti (T t); } public class CurriedCalc {public static void main (String [] args) {System.out.println (calc (1) .apply (2) .apply (3) .apply (4)); } statinė funkcija> calc (galutinis sveikasis skaičius a) {grąžinti naują funkciją> () {@ Nepaisyti viešosios funkcijos taikyti (galutinis sveikasis skaičius b) {grąžinti naują funkciją() {@Paisyti viešąją funkciją taikoma (galutinis sveikasis skaičius c) {grąžinti naują funkciją () {@Paisyti viešąjį sveikąjį skaičių taikyti (sveikasis skaičius d) {grąžinti (a + b) * (c + d); }}; }}; }}; }}

6 sąraše naudojama karija, kad būtų įvertinta funkcija f (a, b, c, d) = (a + b) * (c + d). Duota išraiška apskaičiuoti (1). taikyti (2). taikyti (3). taikyti (4), ši funkcija taikoma taip:

  1. f (1, b, c, d) = g (b, c, d) = (1 + b) * (c + d)
  2. g (2, c, d) = h (c, d) = (1 + 2) * (c + d)
  3. h (3, d) = i (d) = (1 + 2) * (3 + d)
  4. i (4) = (1 + 2) * (3 + 4)

Sudaryti 6 sąrašą:

javac CurriedCalc.java

Paleiskite gautą programą:

java CurriedCalc

Turėtumėte stebėti šį rezultatą:

21

Kadangi curry yra susijęs su daliniu funkcijos taikymu, nesvarbu, kokia tvarka naudojami argumentai. Pavyzdžiui, užuot praleidęs a į apskaičiuoti () ir d į labiausiai įdėtą kreiptis () metodas (kuris atlieka skaičiavimą), mes galime pakeisti šiuos parametrų pavadinimus. Tai sukeltų d c b a vietoj a b c d, bet vis tiek būtų pasiektas tas pats 21. (Šios pamokos šaltinio kode yra alternatyvi CurriedCalc.)

Funkcinis programavimas „Java 8“

Funkcinis programavimas prieš „Java 8“ nėra gražus. Norint sukurti, perduoti funkciją ir (arba) grąžinti funkciją iš pirmos klasės funkcijos, reikia per daug kodo. Ankstesnėse „Java“ versijose taip pat trūksta iš anksto nustatytų funkcinių sąsajų ir aukščiausios klasės funkcijų, tokių kaip filtras ir žemėlapis.

„Java 8“ iš esmės sumažina daugiažodiškumą, pateikdama „lambdas“ ir metodų nuorodas į „Java“ kalbą. Jis taip pat siūlo iš anksto nustatytas funkcines sąsajas ir filtrus, žemėlapius, redukcijas ir kitas pakartotinai naudojamas pirmos klasės funkcijas galima pasiekti naudojant „Streams“ API.

Mes kartu apžvelgsime šiuos patobulinimus kitose skiltyse.

Lambda rašymas Java kodu

A lambda yra išraiška, apibūdinanti funkciją, žymint funkcinės sąsajos įgyvendinimą. Štai pavyzdys:

() -> System.out.println („mano pirmoji lambda“)

Iš kairės į dešinę, () identifikuoja oficialų „lambda“ parametrų sąrašą (parametrų nėra), -> reiškia lambda išraišką ir System.out.println („mano pirmoji lambda“) yra lambda kūnas (kodas, kurį reikia įvykdyti).

Lambda turi tipo, kuri yra bet kuri funkcinė sąsaja, kuriai „lambda“ yra įgyvendinimas. Vienas iš tokių tipų yra java.lang. Bėgama, nes Bėgama's niekinis bėgimas () metodas taip pat turi tuščią oficialių parametrų sąrašą:

Vykdomas r = () -> System.out.println („mano pirmoji lambda“);

Galite perduoti lambdą bet kur, kur a Bėgama reikalingas argumentas; pavyzdžiui, Siūlas (Runnable r) konstruktorius. Darant prielaidą, kad įvyko ankstesnė užduotis, galite ją išlaikyti r šiam konstruktoriui taip:

naujas siūlas (r);

Arba galite perduoti lambda tiesiai konstruktoriui:

nauja gija (() -> System.out.println („mano pirmoji lambda“));

Tai tikrai kompaktiškesnė nei ankstesnė „Java 8“ versija:

nauja gija (nauja „Runnable“) {@Paisyti viešą negaliojančią paleidimą () {System.out.println ("mano pirmoji lambda");}});

Lambda failų filtras

Ankstesnis mano demonstravimas apie aukštesnio lygio funkcijas pristatė failų filtrą, pagrįstą anoniminiu vidiniu klase. Štai „lambda“ atitikmuo:

File [] txtFiles = new File ("."). ListFiles (p -> p.getAbsolutePath (). EndWith ("txt"));

Grąžinkite teiginius lambda išraiškomis

1 dalyje paminėjau, kad funkcinės programavimo kalbos veikia su išraiškomis, o ne teiginiais. Iki „Java 8“ galite iš esmės pašalinti teiginius funkciniame programavime, tačiau negalėjote pašalinti grįžti pareiškimas.

Pirmiau pateiktas kodo fragmentas rodo, kad lambda nereikia a grįžti sakinys grąžina vertę (šiuo atveju loginė tikroji / klaidinga reikšmė): tiesiog nurodykite išraišką be grįžti [ir pridėkite] kabliataškį. Vis dėlto jums reikės kelių sakinių lambdų grįžti pareiškimas. Tokiais atvejais turite įdėti lambda kūną tarp petnešų taip (nepamirškite kabliataškio, kad užbaigtumėte teiginį):

File [] txtFiles = new File ("."). ListFiles (p -> {return p.getAbsolutePath (). EndWith ("txt");});

„Lambdas“ su funkcinėmis sąsajomis

Turiu dar du pavyzdžius, kurie iliustruoja lambdų glaustumą. Pirmiausia peržiūrėkime pagrindinis () metodas iš Rūšiuoti 2 sąraše rodoma programa:

public static void main (String [] args) {String [] internalplanets = {"Merkurijus", "Venera", "Žemė", "Marsas"}; sąvartynas (vidinės planetos); rūšiuoti (vidinės planetos, (e1, e2) -> e1.compareTo (e2)); sąvartynas (vidinės planetos); rūšiuoti (vidinės planetos, (e1, e2) -> e2.compareTo (e1)); sąvartynas (vidinės planetos); }

Mes taip pat galime atnaujinti apskaičiuoti () metodas iš CurriedCalc 6 sąraše rodoma programa:

statinė funkcija> apskaičiuoti (sveikasis skaičius a) {grąžinti b -> c -> d -> (a + b) * (c + d); }

Bėgama, „FileFilter“ir Lyginamasis yra pavyzdžiai funkcinės sąsajos, kurios apibūdina funkcijas. „Java 8“ įformino šią koncepciją reikalaudama, kad funkcinė sąsaja būtų pažymėta java.lang.FunctionalInterface anotacijos tipas, kaip ir @ Funkcinė sąsaja. Sąsaja, kuri yra pažymėta šiuo tipu, turi deklaruoti tiksliai vieną abstraktų metodą.

Galite naudoti iš anksto nustatytas „Java“ funkcines sąsajas (aptartas vėliau), arba galite lengvai nurodyti savo:

@FunctionalInterface sąsaja Funkcija {R taikyti (T t); }

Tada galite naudoti šią funkcinę sąsają, kaip parodyta čia:

public static void main (String [] args) {System.out.println (getValue (t -> (int) (Math.random () * t), 10)); System.out.println (getValue (x -> x * x, 20)); } static Integer getValue (Funkcija f, int x) {return f.apply (x); }

Pirmą kartą naudojate lambdas?

Jei dar nesinaudojote „lambdas“, jums gali prireikti daugiau informacijos, kad suprastumėte šiuos pavyzdžius. Tokiu atveju peržiūrėkite mano tolesnį „lambda“ ir funkcinių sąsajų įvadą skiltyje „Pradėkite naudoti„ lambda “išraiškas„ Java “. Taip pat rasite daug naudingų tinklaraščių įrašų šia tema. Vienas pavyzdžių yra „Funkcinis programavimas su„ Java 8 “funkcijomis“, kuriame autorius Edwinas Dalorzo parodo, kaip „Java 8“ naudoti lambda išraiškas ir anonimines funkcijas.

Lambda architektūra

Kiekviena „lambda“ galiausiai yra kažkokios klasės, sukurtos užkulisiuose, pavyzdys. Naršykite šiuos išteklius, kad sužinotumėte daugiau apie lambda architektūrą:

  • „Kaip veikia lambdas ir anoniminės vidinės klasės“ (Martin Farrell, DZone)
  • „Lambdas Javoje: žvilgsnis po gaubtu“ (Brian Goetz, GOTO)
  • "Kodėl" Java 8 "lambdas yra iškviečiami naudojant invokeynamic?" (Kamino perpildymas)

Manau, kad „Java“ kalbos architekto Briano Goetzo vaizdo pristatymas apie tai, kas vyksta po gaubtu su lambdomis, jums bus ypač patrauklus.

Metodo nuorodos „Java“

Kai kurie lambdai naudojasi tik esamu metodu. Pavyzdžiui, taikoma ši lambda System.out's negaliojantis spausdinimas (-ai) metodas pagal vienintelį lambda argumentą:

(String s) -> System.out.println (s)

Lambda pristato (Eilutės) kaip jos oficialų parametrų sąrašą ir kodą, kurio System.out.println (s) išraiškos atspaudai svertė standartiniam išvesties srautui.

Norėdami išsaugoti klavišų paspaudimus, lambda galite pakeisti a metodo nuoroda, kuri yra glausta nuoroda į esamą metodą. Pavyzdžiui, ankstesnį kodo fragmentą galite pakeisti taip:

System.out :: println

Čia :: reiškia tai System.out's void println (eilutės) metodas yra nurodomas. Metodo nuoroda suteikia daug trumpesnį kodą, nei pasiekėme su ankstesne lambda.

Rūšiavimo metodo nuoroda

Anksčiau aš parodžiau „lambda“ versiją Rūšiuoti programa iš 2 sąrašo. Čia yra tas pats kodas, parašytas metodo nuoroda:

public static void main (String [] args) {String [] internalplanets = {"Merkurijus", "Venera", "Žemė", "Marsas"}; sąvartynas (vidinės planetos); rūšiuoti (vidinės planetos, eilutė :: palygintiTo); sąvartynas (vidinės planetos); rūšiuoti (vidinės planetos, Comparator.comparing (String :: toString) .reversed ()); sąvartynas (vidinės planetos); }

Stygos :: palyginti metodo referencinė versija yra trumpesnė nei "lambda" versija (e1, e2) -> e1.palyginti su (e2). Tačiau atkreipkite dėmesį, kad norint sukurti lygiavertę atvirkštinės eilės rūšiavimą, reikalinga ilgesnė išraiška, kuri taip pat apima metodo nuorodą: Stygos :: toString. Užuot nurodęs Stygos :: toString, Galėčiau nurodyti atitikmenį s -> s.toString () lambda.

Daugiau apie metodo nuorodas

Metodo nuorodų yra daug daugiau, nei galėčiau aprėpti ribotoje erdvėje. Norėdami sužinoti daugiau, peržiūrėkite mano įvadą į metodinių nuorodų, susijusių su statiniais metodais, nestatiniais metodais ir konstruktoriais, rašymą skyriuje „Pradėkite naudoti metodų nuorodas„ Java “.

Iš anksto nustatytos funkcinės sąsajos

„Java 8“ pristatė iš anksto nustatytas funkcines sąsajas (java.util.funkcija), kad kūrėjai neturėtų savo funkcinių sąsajų, skirtų bendroms užduotims atlikti. Štai keli pavyzdžiai:

  • Vartotojas funkcinė sąsaja reiškia operaciją, kuri priima vieną įvesties argumentą ir negrąžina rezultato. Jo negaliojantis sutikimas (T t) metodas šią operaciją atlieka argumentais t.
  • Funkcija funkcinė sąsaja reiškia funkciją, kuri priima vieną argumentą ir pateikia rezultatą. Jo R taikoma (T t) metodas šią funkciją taiko argumentams t ir grąžina rezultatą.
  • Spėti funkcinė sąsaja reiškia a tarinys (Būlio vertinama funkcija) vieno argumento. Jo loginis bandymas (T t) metodas įvertina šį teiginį argumentuodamas t ir grąžina teisingą ar klaidingą.
  • Tiekėjas funkcinė sąsaja reiškia rezultatų tiekėją. Jo T gauti () metodas negauna jokių argumentų, bet pateikia rezultatą.

DienosMėnesis taikymas 1 sąraše atskleidė išsamų Funkcija sąsaja. Pradėdami nuo „Java 8“, galite pašalinti šią sąsają ir importuoti identišką iš anksto nustatytą Funkcija sąsaja.

Daugiau apie iš anksto nustatytas funkcines sąsajas

"Pradėkite nuo" lambda "išraiškų" Java "pateikia pavyzdžius Vartotojas ir Spėti funkcinės sąsajos. Peržiūrėkite tinklaraščio įrašą „Java 8 - tingus argumentų įvertinimas“, kad sužinotumėte apie įdomų jo naudojimą Tiekėjas.

Be to, nors iš anksto nustatytos funkcinės sąsajos yra naudingos, jos taip pat kelia keletą problemų. Tinklaraštininkas Pierre-Yvesas Saumontas paaiškina kodėl.

Funkcinės API: srautai

„Java 8“ pristatė srautų API, kad palengvintų nuoseklų ir lygiagretų duomenų elementų apdorojimą. Ši API yra pagrįsta srautai, kur srautas yra elementų seka, kilusi iš šaltinio ir palaikanti nuoseklias ir lygiagrečias agregavimo operacijas. A šaltinis saugo elementus (pvz., kolekciją) arba generuoja elementus (pvz., atsitiktinių skaičių generatorius). An suvestinė yra rezultatas, apskaičiuotas pagal kelias įvesties reikšmes.

Srautas palaiko tarpines ir terminalines operacijas. An tarpinė operacija grąžina naują srautą, tuo tarpu a terminalo veikimas sunaudoja srautą. Operacijos yra sujungtos į a dujotiekis (per metodų grandinę). Dujotiekis prasideda šaltiniu, po kurio atliekamos nulinės ar daugiau tarpinių operacijų, ir baigiasi terminalo operacija.

Srautai yra a pavyzdys funkcinė API. Jis siūlo filtravimo, žemėlapio sudarymo, mažinimo ir kitas daugkartinio naudojimo pirmos klasės funkcijas. Trumpai pademonstravau šią API Darbuotojai 1 sąrašo 1 sąraše rodoma programa. 7 sąraše pateikiamas kitas pavyzdys.

Sąrašas 7. Funkcinis programavimas naudojant srautus (StreamFP.java)

importuoti java.util.Random; importuoti java.util.stream.IntStream; public class StreamFP {public static void main (String [] args) {new Random (). ints (0, 11) .limit (10) .filter (x -> x% 2 == 0) .forEach (System.out :: println); System.out.println (); Styginiai [] miestai = {"Niujorkas", "Londonas", "Paryžius", "Berlynas", "BrasÌlia", "Tokijas", "Pekinas", "Jeruzalė", "Kairas", "Rijadas", "Maskva" }; IntStream.range (0, 11) .mapToObj (i -> miestai [i]) .forEach (System.out :: println); System.out.println (); System.out.println (IntStream.range (0, 10) .sumažinti (0, (x, y) -> x + y)); System.out.println (IntStream.range (0, 10) .reduce (0, Sveikasis skaičius: suma)); }}

pagrindinis () metodas pirmiausia sukuria pseudopatybinių sveikų skaičių srautą, prasidedantį 0 ir baigiant 10. Srautas ribojamas tiksliai 10 sveikųjų skaičių. filtras() pirmos klasės funkcija gauna lambdą kaip jos predikatinį argumentą. Predikatas pašalina nelyginius sveikus skaičius iš srauto. Galiausiai kiekvienam() pirmos klasės funkcija spausdina kiekvieną lyginį skaičių į standartinę išvestį per System.out :: println metodo nuoroda.

pagrindinis () metodas sukuria sveiko skaičiaus srautą, kuris sukuria nuoseklų skaičių skaičių intervalą, prasidedantį 0 ir baigiant 10. The mapToObj () pirmos klasės funkcija gauna „lambda“, kuris susieja skaičių su lygiaverte eilute, esančia sveikojo skaičiaus rodyklėje miestuose masyvas. Po to miesto pavadinimas į standartinę išvestį siunčiamas per kiekvienam() pirmos klasės funkcija ir jos System.out :: println metodo nuoroda.

Galiausiai, pagrindinis () demonstruoja sumažinti () pirmos klasės funkcija. Sveiko skaičiaus srautas, kuriantis tą patį skaičių skaičių diapazoną, kaip ir ankstesniame pavyzdyje, sumažinamas iki jų verčių sumos, kuri vėliau išvedama.

Tarpinių ir terminalų operacijų nustatymas

Kiekvienas iš riba (), filtras(), diapazonas()ir mapToObj () yra tarpinės operacijos, tuo tarpu kiekvienam() ir sumažinti () yra terminalo operacijos.

Sudarykite 7 sąrašą taip:

javac StreamFP.java

Paleiskite gautą programą taip:

java StreamFP

Aš pastebėjau tokį vieno važiavimo rezultatą:

0 2 10 6 0 8 10 Niujorkas Londonas Paryžius Berlynas BrasÌlia Tokijas Pekinas Jeruzalė Kairas Rijadas Maskva 45 45

Galbūt tikėjotės 10, o ne 7 pseudopatomi atsitiktiniai skaičiai (nuo 0 iki 10, dėka diapazonas (0, 11)) pasirodyti išvesties pradžioje. Po visko, riba (10) atrodo, kad bus rodoma 10 sveikųjų skaičių. Tačiau taip nėra. nors riba (10) skambučio rezultatas yra lygiai 10 sveikų skaičių srautas, filtras (x -> x% 2 == 0) skambučio metu iš srauto pašalinami nelyginiai sveiki skaičiai.

Daugiau apie srautus

Jei nesate susipažinę su srautais, daugiau informacijos apie šią funkcinę API rasite mano pamokoje, kurioje pristatoma „Java SE 8“ naujoji srautų API.

Apibendrinant

Daugelis „Java“ kūrėjų nevers grynojo funkcinio programavimo tokia kalba kaip „Haskell“, nes tai labai skiriasi nuo įprastos imperatyvios, į objektus orientuotos paradigmos. „Java 8“ funkcinės programavimo galimybės yra skirtos tam, kad būtų galima panaikinti šią spragą, leidžiančios „Java“ kūrėjams rašyti kodą, kurį būtų lengviau suprasti, prižiūrėti ir išbandyti. Funkcinis kodas taip pat yra daugkartinio naudojimo ir labiau tinka lygiagrečiam „Java“ apdorojimui. Turint visas šias paskatas, nėra jokios priežasties neįtraukti „Java“ funkcinių programavimo parinkčių į „Java“ kodą.

Parašykite funkcinę „Bubble Sort“ programą

Funkcinis mąstymas yra Nealo Fordo sugalvotas terminas, kuris reiškia kognityvinį perėjimą nuo objektu orientuotos paradigmos prie funkcinės programavimo paradigmos. Kaip matėte šioje pamokoje, galima daug sužinoti apie funkcinį programavimą, perrašant į objektą orientuotą kodą naudojant funkcinius metodus.

Apibendrinkite tai, ko išmokote iki šiol, peržiūrėdami programą „Rūšiuoti“ iš 2 sąrašo. Šiame trumpame patarime aš jums parodysiu, kaip parašykite grynai funkcinį burbulų rūšiavimą, pirmiausia naudodami prieš „Java 8“ naudojamus metodus, o tada naudodami „Java 8“ funkcines savybes.

Šią istoriją „Funkcinis programavimas„ Java “kūrėjams, 2 dalis“ iš pradžių paskelbė „JavaWorld“.