Programmēšana

Funkcionālā programmēšana Java izstrādātājiem, 2. daļa

Laipni lūdzam atpakaļ šajā divdaļīgajā apmācībā, kas ievieš funkcionālu programmēšanu Java kontekstā. Programmā Java izstrādātāju funkcionālā programmēšana, 1. daļa, es izmantoju JavaScript piemērus, lai sāktu darbu ar piecām funkcionālajām programmēšanas metodēm: tīras funkcijas, augstākas pakāpes funkcijas, slinks novērtējums, slēgšana un karija. Šo piemēru uzrādīšana JavaScript ļāva mums koncentrēties uz metodēm vienkāršākā sintaksē, neiekļūstot Java sarežģītākajās funkcionālās programmēšanas iespējās.

2. daļā mēs atkārtoti aplūkosim šīs metodes, izmantojot Java kodu, kas datēts pirms Java 8. Kā redzēsit, šis kods ir funkcionāls, taču to nav viegli rakstīt vai lasīt. Jūs iepazīstināsiet arī ar jaunajām funkcionālajām programmēšanas funkcijām, kas pilnībā tika integrētas Java valodā Java 8; proti, lambdas, metodes atsauces, funkcionālās saskarnes un Streams API.

Šajā apmācībā mēs atkārtoti aplūkosim 1. daļas piemērus, lai redzētu, kā salīdzināt JavaScript un Java piemērus. Jūs redzēsiet arī to, kas notiek, kad es atjauninu dažus no iepriekšējiem Java 8 piemēriem ar funkcionālām valodas funkcijām, piemēram, lambdas un metožu atsaucēm. Visbeidzot, šajā apmācībā ir ietverts praktisks vingrinājums, kas paredzēts, lai jums palīdzētu praktizē funkcionālo domāšanu, ko jūs izdarīsit, pārveidojot objektorientētā Java koda fragmentu tā funkcionālajā ekvivalentā.

lejupielādēt Iegūt kodu Lejupielādējiet šajā apmācībā avota kodu, piemēram, lietojumprogrammām. Izveidoja Jeff Friesen JavaWorld.

Funkcionāla programmēšana ar Java

Daudzi izstrādātāji to neapzinās, taču pirms Java 8 bija iespējams rakstīt funkcionālas programmas Java. Lai būtu labi noapaļots Java funkcionālās programmēšanas skats, ātri pārskatīsim funkcionālās programmēšanas funkcijas, kas bija pirms Java 8. Tie ir nonākuši, jūs, iespējams, vairāk novērtēsiet, kā jaunās Java 8 ieviestās funkcijas (piemēram, lambdas un funkcionālās saskarnes) ir vienkāršojušas Java pieeju funkcionālajai programmēšanai.

Java funkcionālās programmēšanas atbalsta robežas

Pat uzlabojot Java 8 funkcionālos programmēšanas uzlabojumus, Java joprojām ir obligāta, objektorientēta programmēšanas valoda. Trūkst diapazona veidu un citu funkciju, kas padarītu to funkcionālāku. Java tiek arī nominēta ar nominatīvu rakstīšanu, kas ir noteikums, ka katram tipam ir jābūt nosaukumam. Neskatoties uz šiem ierobežojumiem, izstrādātājiem, kuri izmanto Java funkcionālās funkcijas, joprojām ir izdevīgi, ja viņi var rakstīt kodolīgāku, atkārtoti lietojamu un lasāmu kodu.

Funkcionāla programmēšana pirms Java 8

Anonīmās iekšējās klases kopā ar saskarnēm un slēgumiem ir trīs vecākas funkcijas, kas atbalsta funkcionālu programmēšanu vecākās Java versijās:

  • Anonīmas iekšējās klases ļaujiet jums nodot funkcionalitāti (ko raksturo saskarnes) metodēm.
  • Funkcionālās saskarnes ir saskarnes, kas apraksta funkciju.
  • Slēgumi ļauj piekļūt mainīgajiem to ārējās darbības jomās.

Turpmākajās sadaļās mēs pārskatīsim piecus 1. daļā ieviestos paņēmienus, bet izmantojot Java sintaksi. Jūs redzēsiet, kā katra no šīm funkcionālajām metodēm bija iespējama pirms Java 8.

Tīro funkciju rakstīšana Java valodā

1. saraksts parāda pirmkodu lietojumprogrammas paraugam, DaysInMonth, kas ir rakstīts, izmantojot anonīmu iekšējo klasi un funkcionālo saskarni. Šī lietojumprogramma parāda, kā rakstīt tīru funkciju, kas Java bija sasniedzama ilgi pirms Java 8.

Saraksts 1. Tīrā funkcija Java (DaysInMonth.java)

saskarne Funkcija {R piemērot (T t); } public class DaysInMonth {public static void main (String [] args) {Function dim = new Function () {@Override public Integer Apply (Integer month) {return new Integer [] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} [mēnesis]; }}; System.out.printf ("Aprīlis:% d% n", dim.apply (3)); System.out.printf ("Augusts:% d% n", dim.apply (7)); }}

Vispārīgais Funkcija 1. saraksta saskarne apraksta funkciju ar vienu tipa parametru T un atgriešanās tipa tips R. The Funkcija interfeiss deklarē R piemērot (T t) metodi, kas šo funkciju pielieto dotajam argumentam.

The galvenais () metode izstaro anonīmu iekšējo klasi, kas īsteno Funkcija interfeiss. The pieteikties () metode unboxes mēnesī un to izmanto, lai indeksētu dienas skaitļu masīvu masīvu. Tiek atgriezts veselais skaitlis šajā indeksā. (Es vienkāršības labad ignorēju garos gadus.)

galvenais () next izpilda šo funkciju divas reizes, atsaucoties pieteikties () atgriezt aprīļa un augusta dienu skaitīšanu. Šie skaitļi pēc tam tiek izdrukāti.

Mums ir izdevies izveidot funkciju un tajā tīru funkciju! Atgādinām, ka a tīra funkcija atkarīgs tikai no tā argumentiem un nav ārēja stāvokļa. Blakusparādības nav.

Apkopojiet 1. sarakstu šādi:

javac DaysInMonth.java

Palaidiet iegūto lietojumprogrammu šādi:

java DaysInMonth

Jums jāievēro šāda izeja:

Aprīlis: 30. augusts: 31

Augstākas kārtas funkciju rakstīšana Java valodā

Tālāk mēs aplūkosim augstākas pakāpes funkcijas, kas pazīstamas arī kā pirmās klases funkcijas. Atcerieties, ka a augstākas kārtas funkcija saņem funkcijas argumentus un / vai atgriež funkcijas rezultātu. Java saista funkciju ar metodi, kas definēta anonīmā iekšējā klasē. Šīs klases eksemplārs tiek nodots citai Java metodei vai tiek atgriezts no tās, kas kalpo kā augstākas pakāpes funkcija. Šis faila orientētais koda fragments parāda funkcijas nodošanu augstākas pakāpes funkcijai:

File [] txtFiles = new File ("."). ListFiles (new FileFilter () {@Orride public Boolean accept (File pathname) {return pathname.getAbsolutePath (). EndWith ("txt");}});

Šis koda fragments iziet funkciju, kuras pamatā ir java.io.FileFilter funkcionālā saskarne ar java.io.File klases File [] listFiles (FileFilter filtrs) metodi, liekot tai atgriezt tikai tos failus ar txt paplašinājumi.

2. saraksts parāda vēl vienu veidu, kā strādāt ar Java augstākas kārtas funkcijām. Šajā gadījumā kods nodod salīdzināšanas funkciju a kārtot () augstākas kārtas funkcija augošā secības kārtošanai un otra salīdzināšanas funkcija kārtot () lai kārtotu dilstošā secībā.

Saraksts 2. Augstākas kārtas funkcija Java (Sort.java)

importēt java.util.Comparator; publiskā klase Kārtot {public static void main (String [] args) {String [] iekštelpas = {"Merkurs", "Venēra", "Zeme", "Marss"}; izgāztuve (iekšējās planētas); kārtot (iekšējās planētas, jauns Comparator () {@Orride public int salīdzināt (String e1, String e2) {return e1.compareTo (e2);}}); izgāztuve (iekšējās planētas); kārtot (iekšējās planētas, jauns Comparator () {@Orride public int salīdzināt (String e1, String e2) {return e2.compareTo (e1);}}); izgāztuve (iekšējās planētas); } static void dump (T [] masīvs) {for (T elements: masīvs) System.out.println (elements); System.out.println (); } static void sort (T [] masīvs, salīdzinātāja cmp) {for (int pass = 0; pass  iziet; i--) if (cmp.compare (masīvs [i], masīvs [pass]) <0) mijmaiņas (masīvs, i, pāreja); } static void swap (T [] masīvs, int i, int j) {T temp = masīvs [i]; masīvs [i] = masīvs [j]; masīvs [j] = temp; }}

Sarakstā 2 tiek importēts java.util.Salīdzinātājs funkcionālā saskarne, kas apraksta funkciju, ar kuru var veikt divu patvaļīga, bet identiska tipa objektu salīdzinājumu.

Divas nozīmīgas šī kodeksa daļas ir kārtot () metode (kas ievieš Bubble Sort algoritmu) un kārtot () aicinājumi galvenais () metodi. Lai gan kārtot () tas nebūt nav funkcionāls, tas demonstrē augstākas pakāpes funkciju, kas funkciju - salīdzinātāju - saņem kā argumentu. Tā izpilda šo funkciju, atsaucoties uz to salīdzināt() metodi. Divi šīs funkcijas gadījumi tiek nodoti divos kārtot () zvana galvenais ().

Sastādiet 2. sarakstu šādi:

javac Sort.java

Palaidiet iegūto lietojumprogrammu šādi:

java Kārtot

Jums jāievēro šāda izeja:

Dzīvsudraba Venēra Zeme Mars Mars Earth Mars Mercury Venus Venus Mercury Mars Earth

Slinks vērtējums Java valodā

Slinks vērtējums ir vēl viena funkcionāla programmēšanas tehnika, kas Java 8 nav jauns. Šis paņēmiens aizkavē izteiksmes novērtēšanu, līdz tās vērtība ir nepieciešama. Vairumā gadījumu Java ar nepacietību vērtē izteiksmi, kas ir saistīta ar mainīgo. Java atbalsta slinku novērtēšanu šādai specifiskai sintaksei:

  • Būla && un || operatori, kuri nevērtēs viņu labo operandu, ja kreisais operands ir nepatiess (&&) vai patiess (||).
  • The ?: operators, kurš novērtē Būla izteiksmi un pēc tam novērtē tikai vienu no divām alternatīvām izteiksmēm (saderīga tipa), pamatojoties uz Būla izteiksmes patieso / nepatieso vērtību.

Funkcionālā programmēšana veicina programmēšanu, kas orientēta uz izteiksmi, tāpēc vēlēsities pēc iespējas vairāk izvairīties no pārskatu izmantošanas. Piemēram, pieņemsim, ka vēlaties aizstāt Java ja-cits paziņojums ar ifThenElse () metodi. 3. saraksts parāda pirmo mēģinājumu.

3. saraksts. Dedzīgas vērtēšanas piemērs Java (EagerEval.java)

public class EagerEval {public static void main (String [] args) {System.out.printf ("% d% n", ifThenElse (true, kvadrāts (4), kubs (4))); System.out.printf ("% d% n", ifThenElse (false, kvadrāts (4), kubs (4))); } statiskais int kubs (int x) {System.out.println ("kubā"); atgriešanās x * x * x; } static int ifThenElse (Būla predikāts, int onTrue, int onFalse) {return (predikāts)? onTrue: onFalse; } static int square (int x) {System.out.println ("kvadrātā"); atgriešanās x * x; }}

3. saraksts definē ifThenElse () metode, kas ņem Būla predikātu un veselu skaitļu pāri, atgriežot onTrue vesels skaitlis, kad predikāts ir taisnība un onFalse vesels skaitlis citādi.

3. saraksts arī nosaka kubs () un kvadrāts() metodes. Attiecīgi šīs metodes kubu un kvadrātu veido veselu skaitli un atgriež rezultātu.

The galvenais () metodi izsauc ifThenElse (taisnība, kvadrāts (4), kubs (4)), uz kuru vajadzētu atsaukties tikai kvadrāts (4), kam seko ifThenElse (false, kvadrāts (4), kubs (4)), uz kuru vajadzētu atsaukties tikai kubs (4).

Sastādiet 3. sarakstu šādi:

javac EagerEval.java

Palaidiet iegūto lietojumprogrammu šādi:

java EagerEval

Jums jāievēro šāda izeja:

kvadrātā 16. kubā kvadrātā 64. kubā

Rezultāts parāda, ka katrs ifThenElse () izsaukuma rezultāti tiek izpildīti abās metodēs, neatkarīgi no Būla izteiksmes. Mēs nevaram izmantot ?: operatora slinkums, jo Java ar nepacietību vērtē metodes argumentus.

Lai gan nav iespējams izvairīties no vēlmes novērtēt metodes argumentus, mēs tomēr varam to izmantot ?:ir slinks novērtējums, lai nodrošinātu tikai to kvadrāts() vai kubs () tiek saukts. 4. saraksts parāda, kā.

4. uzskaitījums: slinka vērtējuma piemērs Java valodā (LazyEval.java)

saskarne Funkcija {R piemērot (T t); } public class LazyEval {public static void main (String [] args) {Funkciju kvadrāts = new Function () {{System.out.println ("SQUARE"); } @ Pārvarēt publisko veselo skaitli (Integer t) {System.out.println ("kvadrātā"); atgriešanās t * t; }}; Funkciju kubs = jauna funkcija () {{System.out.println ("CUBE"); } @ Pārvarēt publisko veselo skaitli (Integer t) {System.out.println ("kubā"); atgriešanās t * t * t; }}; System.out.printf ("% d% n", ifThenElse (true, kvadrāts, kubs, 4)); System.out.printf ("% d% n", ifThenElse (false, kvadrāts, kubs, 4)); } static R ifThenElse (Būla predikāts, Funkcija onTrue, Funkcija onFalse, T t) {return (predikāts? onTrue.apply (t): onFalse.apply (t)); }}

Uzskaita 4 pagriezienus ifThenElse () augstākas pakāpes funkcijā, paziņojot, ka šī metode saņem pāris Funkcija argumenti. Lai gan šie argumenti tiek dedzīgi novērtēti, kad tie tiek nodoti ifThenElse (), ?: operators liek izpildīt tikai vienu no šīm funkcijām (izmantojot pieteikties ()). Apkopojot un palaižot lietojumprogrammu, darbā var redzēt gan nepacietīgu, gan slinku novērtējumu.

Sastādiet 4. sarakstu šādi:

javac LazyEval.java

Palaidiet iegūto lietojumprogrammu šādi:

java LazyEval

Jums jāievēro šāda izeja:

Kvadrāta KUBS 16. kvadrātā 64. kubā

Slinks atkārtotājs un daudz kas cits

Nīla Forda "Slinkums, 1. daļa: Slinka vērtējuma izpēte Java" sniedz lielāku ieskatu slinkajā vērtēšanā. Autore iepazīstina ar Java balstītu slinko iteratoru kopā ar pāris uz slinkumu orientētiem Java ietvariem.

Aizverumi Java valodā

Anonīma iekšējās klases instance ir saistīta ar a slēgšana. Ir jādeklarē ārējās darbības jomas mainīgie galīgais vai (sākot ar Java 8) faktiski galīgs (kas nozīmē, ka pēc inicializācijas tas nav modificēts), lai tā būtu pieejama. Apsveriet iespēju norādīt 5. sarakstu.

5. saraksts. Java slēgšanas piemērs (PartialAdd.java)

saskarne Funkcija {R piemērot (T t); } public class PartialAdd {Funkcija pievienot (gala int x) {Funkcija daļējaPievienot = jauna funkcija () {@ Pārvarēt publisko veselo skaitli piemērot (Integer y) {return y + x; }}; atgriešanās daļējaPievienot; } 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. saraksts ir Java ekvivalents slēgšanai, kuru es iepriekš uzrādīju JavaScript (skat. 1. daļas 8. sarakstu). Šis kods deklarē pievienot () augstākas pakāpes funkcija, kas atgriež funkciju, lai veiktu daļēju pievienot () funkciju. The pieteikties () metode piekļūst mainīgajam x ārējā darbības jomā pievienot (), kas jādeklarē galīgais pirms Java 8. Kods darbojas gandrīz tāpat kā JavaScript ekvivalents.

Sastādiet 5. sarakstu šādi:

javac PartialAdd.java

Palaidiet iegūto lietojumprogrammu šādi:

java PartialAdd

Jums jāievēro šāda izeja:

15 25

Karijs Java valodā

Jūs, iespējams, pamanījāt, ka PartialAdd 5. sarakstā parāda ne tikai slēgšanu. Tas arī parāda karija, kas ir veids, kā daudzargumentu funkcijas vērtējumu pārvērst vienvērtīgu vienargumentu funkciju secības novērtējumā. Abi pa.dd (10) un pa. pievienot (20) 5. sarakstā atgriež slēgumu, kurā ierakstīts operands (10 vai 20, attiecīgi) un funkcija, kas veic saskaitīšanu, - otrais operands (5) tiek nodots caur add10.apply (5) vai pievienot20.apply (5).

Karija ļauj mums novērtēt funkciju argumentus pa vienam, izveidojot jaunu funkciju ar vienu soli mazāk katrā solī. Piemēram, PartialAdd lietojumprogrammu, mēs izmantojam šādu funkciju:

f (x, y) = x + y

Mēs varētu vienlaikus izmantot abus argumentus, sniedzot sekojošo:

f (10, 5) = 10 + 5

Tomēr, izmantojot kariju, mēs izmantojam tikai pirmo argumentu, iegūstot šo:

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

Tagad mums ir viena funkcija, g, kas prasa tikai vienu argumentu. Šī ir funkcija, kas tiks novērtēta, kad mēs to izsauksim pieteikties () metodi.

Daļēja piemērošana, nevis daļēja pievienošana

Vārds PartialAdd apzīmē daļēja piemērošana no pievienot () funkciju. Tas nenozīmē daļēju pievienošanu. Karijs ir par funkcijas daļēju pielietošanu. Runa nav par daļēju aprēķinu veikšanu.

Jūs varētu sajaukt, lietojot frāzi "daļēja lietošana", jo īpaši tāpēc, ka 1. daļā es norādīju, ka karija nav tas pats, kas daļēja piemērošana, kas ir process, kurā funkcijai tiek piestiprināti vairāki argumenti, radot vēl vienu mazāka aritāta funkciju. Izmantojot daļēju lietošanu, jūs varat izveidot funkcijas ar vairāk nekā vienu argumentu, bet, izmantojot kariju, katrai funkcijai jābūt tieši vienam argumentam.

5. sarakstā ir sniegts neliels Java balstītas karija piemērs pirms Java 8. Tagad apsveriet CurriedCalc pieteikums 6. sarakstā.

Saraksts 6. Karija veidošana Java kodā (CurriedCalc.java)

saskarne Funkcija {R piemērot (T t); } public class CurriedCalc {public static void main (String [] args) {System.out.println (calc (1) .apply (2) .apply (3) .apply (4)); } statiskā funkcija> calc (galīgais veselais skaitlis a) {return new Function> () {@ Pārvarēt publisko funkciju piemērot (galīgais vesels skaitlis b) {atgriezt jaunu funkciju() {@ Pārvarēt publisko funkciju Pielietot (galīgais vesels skaitlis c) {Atgriezt jaunu funkciju () {@ Pārvarēt Publisko veselu skaitli lietot (Integers d) {Atgriezt (a + b) * (c + d); }}; }}; }}; }}

6. sarakstā tiek izmantota karija, lai novērtētu funkciju f (a, b, c, d) = (a + b) * (c + d). Dota izteiksme aprēķināt (1). lietot (2). lietot (3). lietot (4), šī funkcija tiek veidota šādi:

  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)

Sastādīt 6. sarakstu:

javac CurriedCalc.java

Palaidiet iegūto lietojumprogrammu:

java CurriedCalc

Jums jāievēro šāda izeja:

21

Tā kā karija ir par funkcijas daļēju pielietošanu, nav svarīgi, kādā secībā tiek izmantoti argumenti. Piemēram, tā vietā, lai nodotu garām a uz aprēķināt () un d pie visvairāk ligzdotajiem pieteikties () metodi (kas veic aprēķinu), mēs varētu mainīt šos parametru nosaukumus. Tā rezultātā d c b a tā vietā a b c d, bet tas joprojām sasniegtu to pašu rezultātu 21. (Šīs apmācības avota kods ietver alternatīvo CurriedCalc.)

Funkcionāla programmēšana Java 8

Funkcionāla programmēšana pirms Java 8 nav glīta. Lai izveidotu, nodotu funkciju un / vai atgrieztu funkciju no pirmās klases funkcijas, ir nepieciešams pārāk daudz koda. Iepriekšējās Java versijās trūkst arī iepriekš definētu funkcionālo saskarņu un pirmās klases funkciju, piemēram, filtra un kartes.

Java 8 lielā mērā samazina daudzbalsību, ieviešot lambdas un metožu atsauces uz Java valodu. Tas piedāvā arī iepriekš definētas funkcionālās saskarnes, un tas nodrošina filtru, kartēšanas, samazināšanas un citu atkārtoti lietojamu pirmās klases funkciju pieejamību, izmantojot Streams API.

Mēs kopā aplūkosim šos uzlabojumus nākamajās sadaļās.

Lambdas rakstīšana Java kodā

A lambda ir izteiksme, kas apraksta funkciju, apzīmējot funkcionālās saskarnes ieviešanu. Lūk, piemērs:

() -> System.out.println ("mana pirmā lambda")

No kreisās uz labo () identificē lambda formālo parametru sarakstu (parametru nav), -> apzīmē lambda izteiksmi un System.out.println ("mana pirmā lambda") ir lambda ķermenis (izpildāmais kods).

Lambda ir tips, kas ir jebkura funkcionāla saskarne, kurai lambda ir realizācija. Viens no šādiem veidiem ir java.lang. Skrienams, jo Skrienams's anulēt palaist () metodei ir arī tukšs formālo parametru saraksts:

Skrienama r = () -> System.out.println ("mana pirmā lambda");

Jūs varat iet garām lambda jebkur, kur a Skrienams ir nepieciešams arguments; piemēram, Vītne (Runnable r) konstruktors. Pieņemot, ka ir noticis iepriekšējais uzdevums, jūs varētu nokārtot r šim konstruktoram šādi:

jauns pavediens (r);

Alternatīvi, jūs varētu nodot lambda tieši konstruktoram:

jauns pavediens (() -> System.out.println ("mana pirmā lambda"));

Tas noteikti ir kompaktāks nekā pirms Java 8 versija:

new Thread (new Runnable () {@Override public void run () {System.out.println ("mana pirmā lambda");}});

Uz lambda bāzes failu filtrs

Mana iepriekšējā augstākās pakāpes funkciju demonstrācija parādīja failu filtru, kura pamatā bija anonīma iekšējā klase. Lūk, uz lambda bāzes ekvivalents:

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

Atgriezties paziņojumus lambda izteicienos

1. daļā es minēju, ka funkcionālās programmēšanas valodas darbojas ar izteicieniem, nevis paziņojumiem. Pirms Java 8 jūs lielākoties varētu novērst paziņojumus funkcionālajā programmēšanā, bet nevarēja novērst atgriešanās paziņojums, apgalvojums.

Iepriekš minētais koda fragments parāda, ka lambda nav nepieciešama a atgriešanās paziņojums, lai atgrieztu vērtību (šajā gadījumā Būla patiesa / nepatiesa vērtība): jūs vienkārši norādāt izteiksmi bez atgriešanās [un pievienojiet] semikolu. Tomēr vairāku paziņojumu lambdām jums joprojām būs nepieciešama atgriešanās paziņojums, apgalvojums. Šādos gadījumos lambda ķermenis starp lencēm jānovieto šādi (neaizmirstiet semikolu, lai izbeigtu paziņojumu):

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

Lambdas ar funkcionālajām saskarnēm

Man ir vēl divi piemēri, lai ilustrētu lambdas īsumu. Vispirms apskatīsim galvenais () metode no Kārtot 2. sarakstā redzamā lietojumprogramma:

public static void main (String [] args) {Stīgu [] iekšējās planētas = {"Merkurs", "Venēra", "Zeme", "Marss"}; izgāztuve (iekšējās planētas); kārtot (iekšējās planētas, (e1, e2) -> e1.compareTo (e2)); izgāztuve (iekšējās planētas); kārtot (iekšējās planētas, (e1, e2) -> e2.compareTo (e1)); izgāztuve (iekšējās planētas); }

Mēs varam arī atjaunināt aprēķināt () metode no CurriedCalc 6. sarakstā redzamā lietojumprogramma:

statiskā funkcija> aprēķināt (vesels skaitlis a) {atgriešanās b -> c -> d -> (a + b) * (c + d); }

Skrienams, FileFilter, un Salīdzinātājs ir piemēri funkcionālās saskarnes, kas apraksta funkcijas. Java 8 formalizēja šo koncepciju, pieprasot funkcionālo saskarni anotēt ar java.lang.FunctionalInterface anotācijas veids, kā tas ir @ Funkcionālā saskarne. Interfeisam, kas ir anotēts ar šo tipu, jādeklarē tieši viena abstrakta metode.

Varat izmantot Java iepriekš noteiktās funkcionālās saskarnes (aplūkotas vēlāk) vai arī viegli norādīt savas, šādi:

@FunctionalInterface interfeiss Funkcija {R piemērot (T t); }

Pēc tam jūs varat izmantot šo funkcionālo saskarni, kā parādīts šeit:

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

Vai esat jauns lambdas lietotājs?

Ja esat jauns lambdas lietotājs, jums, iespējams, vajadzēs vairāk informācijas, lai saprastu šos piemērus. Tādā gadījumā skatiet manu turpmāko ievadu par lambdas un funkcionālajām saskarnēm sadaļā "Darba sākšana ar lambda izteiksmēm Java". Jūs atradīsit arī daudzus noderīgus emuāra ierakstus par šo tēmu. Viens piemērs ir "Funkcionāla programmēšana ar Java 8 funkcijām", kurā autors Edvīns Dalorzo parāda, kā Java 8 lietot lambda izteiksmes un anonīmas funkcijas.

Lambda arhitektūra

Katra lambda galu galā ir kādas klases eksemplārs, kas tiek ģenerēts aizkulisēs. Izpētiet šādus resursus, lai uzzinātu vairāk par lambda arhitektūru:

  • "Kā darbojas lambdas un anonīmās iekšējās klases" (Martin Farrell, DZone)
  • "Lambdas Java valodā: palūrēt zem pārsega" (Braiens Gecs, GOTO)
  • "Kāpēc Java 8 lambdas tiek izsauktas, izmantojot invokedynamic?" (Steka pārpilde)

Es domāju, ka jūs īpaši aizraujoši uzskatīsit Java valodas arhitekta Braiena Geca video prezentāciju par to, kas notiek zem pārsega ar lambdām.

Metodes atsauces Java valodā

Dažas lambdas izmanto tikai esošu metodi. Piemēram, izsauc šādu lambda System.out's void println (s) metode uz lambda vienīgo argumentu:

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

Lambda dāvanas (Virkne s) kā tās formālo parametru sarakstu un koda struktūru, kuras System.out.println (s) izteiksmes izdrukas svērtība standarta izejas plūsmai.

Lai saglabātu taustiņsitienus, lambda var aizstāt ar a metodes atsauce, kas ir kompakta atsauce uz esošu metodi. Piemēram, iepriekšējo koda fragmentu varat aizstāt ar šādu:

System.out :: println

Šeit, :: nozīmē to System.out's void println (virkne s) metode ir atsauce. Metodes atsauces rezultāts ir daudz īsāks kods, nekā mēs sasniedzām ar iepriekšējo lambda.

Metodes atsauce kārtot

Iepriekš es parādīju Lambda versiju Kārtot lietojumprogramma no 2. saraksta. Šeit ir tas pats kods, kas rakstīts ar metodes atsauci:

public static void main (String [] args) {Stīgu [] iekšējās planētas = {"Merkurs", "Venēra", "Zeme", "Marss"}; izgāztuve (iekšējās planētas); kārtot (iekšējās planētas, virkne :: salīdzinātTo); izgāztuve (iekšējās planētas); kārtot (iekšējās planētas, Comparator.comparing (String :: toString) .reversed ()); izgāztuve (iekšējās planētas); }

The Virkne :: salīdzināt metodes atsauces versija ir īsāka nekā (e1, e2) -> e1.salīdzināt (e2). Tomēr ņemiet vērā, ka, lai izveidotu līdzvērtīgu apgrieztā secības kārtošanu, kas prasa arī atsauci uz metodi, ir nepieciešama garāka izteiksme: Stīga :: toString. Tā vietā, lai precizētu Stīga :: toString, Es varēju norādīt ekvivalentu s -> s.toString () lambda.

Vairāk par metožu atsaucēm

Metodes atsauces ir daudz vairāk, nekā es varētu aplūkot ierobežotā telpā. Lai uzzinātu vairāk, skatiet manu ievadu par metožu atsauču rakstīšanu statiskām metodēm, nemestiskām metodēm un konstruktoriem sadaļā "Sākt darbu ar metožu atsaucēm Java".

Iepriekš definētas funkcionālās saskarnes

Java 8 ieviesa iepriekš definētas funkcionālās saskarnes (java.util.function), lai izstrādātājiem nebūtu jāizveido mūsu pašu funkcionālās saskarnes kopīgiem uzdevumiem. Šeit ir daži piemēri:

  • The Patērētājs funkcionālā saskarne ir darbība, kas pieņem vienu ievades argumentu un neatgriež rezultātu. Tā anulēt akceptēt (T t) metode veic šo darbību ar argumentu t.
  • The Funkcija funkcionālā saskarne ir funkcija, kas pieņem vienu argumentu un atgriež rezultātu. Tā R piemērot (T t) metode piemēro šo funkciju argumentam t un atgriež rezultātu.
  • The Paredzēt funkcionālā saskarne apzīmē a predikāts (Būla vērtētā funkcija) viens arguments. Tā Būla tests (T t) metode argumentu vērtē šo predikātu t un atgriež patiesu vai nepatiesu.
  • The Piegādātājs funkcionālā saskarne ir rezultātu piegādātājs. Tā T get () metode nesaņem argumentu (-us), bet atgriež rezultātu.

The DaysInMonth pieteikums 1. sarakstā atklāja pilnīgu Funkcija interfeiss. Sākot ar Java 8, jūs varat noņemt šo saskarni un importēt identisko iepriekš definēto Funkcija interfeiss.

Vairāk par iepriekš definētām funkcionālajām saskarnēm

"Darba sākšana ar lambda izteicieniem Java" sniedz Patērētājs un Paredzēt funkcionālās saskarnes. Apskatiet emuāra ierakstu "Java 8 - slinks argumentu novērtējums", lai atklātu interesantu lietojumu Piegādātājs.

Lai gan iepriekš definētās funkcionālās saskarnes ir noderīgas, tās rada arī dažus jautājumus. Emuāru autors Pjērs Īvs Saumonts paskaidro, kāpēc.

Funkcionālās API: straumes

Java 8 ieviesa Streams API, lai atvieglotu secīgu un paralēlu datu vienumu apstrādi. Šī API pamatā ir straumi, kur straume ir elementu secība, kas cēlusies no avota un atbalsta secīgas un paralēlas agregācijas darbības. A avots glabā elementus (piemēram, kolekciju) vai ģenerē elementus (piemēram, nejaušu skaitļu ģeneratoru). An apkopot ir rezultāts, kas aprēķināts no vairākām ievades vērtībām.

Straume atbalsta starpposma un termināla darbības. An starpposma darbība atgriež jaunu straumi, turpretī a termināļa darbība patērē straumi. Darbības ir savienotas ar a cauruļvads (izmantojot metodes ķēdi). Cauruļvads sākas ar avotu, kam seko nulles vai vairāk starpposma darbības, un beidzas ar termināla darbību.

Straumes ir a. Piemērs funkcionālā API. Tas piedāvā filtrēšanu, kartēšanu, samazināšanu un citas atkārtoti izmantojamas pirmās klases funkcijas. Es īsi parādīju šo API Darbinieki lietojumprogramma, kas parādīta 1. daļas sarakstā 1. 7. saraksts piedāvā vēl vienu piemēru.

Saraksts 7. Funkcionālā programmēšana ar straumēm (StreamFP.java)

importēt java.util.Random; importēt 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 (); Stīgu [] pilsētas = {"Ņujorka", "Londona", "Parīze", "Berlīne", "Braslija", "Tokija", "Pekina", "Jeruzaleme", "Kaira", "Rijāda", "Maskava" }; IntStream.range (0, 11) .mapToObj (i -> pilsētas [i]) .forEach (System.out :: println); System.out.println (); System.out.println (IntStream.range (0, 10) .reduce (0, (x, y) -> x + y)); System.out.println (IntStream.range (0, 10) .reduce (0, Integer :: summa)); }}

The galvenais () metode vispirms izveido pseidorandom veselu skaitļu straumi, kas sākas ar 0 un beidzas ar 10. Straume ir ierobežota līdz tieši 10 veseliem skaitļiem. The filtrs () pirmās klases funkcija kā predikātu argumentu saņem lambda. Predikāts no straumes noņem nepāra skaitļus. Visbeidzot katram() pirmās klases funkcija katru pāra veselu skaitli izdrukā standarta izvadē, izmantojot System.out :: println metodes atsauce.

The galvenais () Pēc tam metode izveido veselu skaitļu straumi, kas veido secīgu veselu skaitļu diapazonu, kas sākas ar 0 un beidzas ar 10. The mapToObj () pirmās klases funkcija saņem lambda, kas kartē veselu skaitli līdzvērtīgai virknei veselā skaitļa indeksā pilsētās masīvs. Pēc tam pilsētas nosaukums tiek nosūtīts uz standarta izvadi, izmantojot katram() pirmās klases funkcija un tās System.out :: println metodes atsauce.

Visbeidzot, galvenais () demonstrē samazināt () pirmās klases funkcija. Vesela skaitļa straume, kas rada tādu pašu veselu skaitļu diapazonu kā iepriekšējā piemērā, tiek samazināta līdz to vērtību summai, kas pēc tam tiek izvadīta.

Starpposma un termināļa darbību identificēšana

Katrs no ierobežojums (), filtrs (), diapazons (), un mapToObj () ir starpposma darbības, bet katram() un samazināt () ir termināla operācijas.

Sastādiet 7. sarakstu šādi:

javac StreamFP.java

Palaidiet iegūto lietojumprogrammu šādi:

java StreamFP

Es novēroju šādu izlaidi no viena brauciena:

0 2 10 6 0 8 10 Ņujorka Londona Parīze Berlīne Braslija Tokija Pekina Jeruzaleme Kaira Rijāda Maskava 45 45

Jūs, iespējams, sagaidījāt 10, nevis 7 pseidorandom pat veselus skaitļus (sākot no 0 līdz 10, pateicoties diapazons (0, 11)), kas parādās izejas sākumā. Galu galā, ierobežojums (10) šķiet, norāda, ka tiks izvadīti 10 veseli skaitļi. Tomēr tas tā nav. Lai gan ierobežojums (10) zvana rezultāts ir tieši 10 veselu skaitļu straume, filtrs (x -> x% 2 == 0) zvana rezultātā no straumes tiek noņemti nepāra skaitļi.

Vairāk par straumēm

Ja jums nav zināms Stream, skatiet manu apmācību, kurā iepazīstina ar Java SE 8 jauno Streams API, lai uzzinātu vairāk par šo funkcionālo API.

Noslēgumā

Daudzi Java izstrādātāji neveiks tīru funkcionālu programmēšanu tādā valodā kā Haskell, jo tā tik ļoti atšķiras no pazīstamās imperatīvās, objektorientētās paradigmas. Java 8 funkcionālās programmēšanas iespējas ir paredzētas, lai pārvarētu šo plaisu, ļaujot Java izstrādātājiem rakstīt kodu, kuru ir vieglāk saprast, uzturēt un pārbaudīt. Funkcionālais kods ir arī vairākkārt lietojams un vairāk piemērots paralēlai apstrādei Java. Izmantojot visus šos stimulus, patiešām nav iemesla neiekļaut Java funkcionālās programmēšanas opcijas jūsu Java kodā.

Uzrakstiet funkcionālu Bubble Sort lietojumprogrammu

Funkcionāla domāšana ir Nīla Forda izdomāts termins, kas apzīmē kognitīvo pāreju no objektorientētās paradigmas uz funkcionālās programmēšanas paradigmu. Kā jūs redzējāt šajā apmācībā, ir iespējams uzzināt daudz jauna par funkcionālo programmēšanu, pārrakstot objektorientētu kodu, izmantojot funkcionālās metodes.

Apkopojiet līdz šim apgūto, atkārtoti apmeklējot programmu Kārtot no 2. saraksta. Šajā īsajā padomā es parādīšu, kā uzrakstiet tīri funkcionālu burbuļu šķirošanu, vispirms izmantojot pirms Java 8 paņēmienus un pēc tam izmantojot Java 8 funkcionālās funkcijas.

Šo stāstu "Funkcionālā programmēšana Java izstrādātājiem, 2. daļa" sākotnēji publicēja JavaWorld.

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