Funkcionális programozás Java-ban

1. Bemutatkozás

Ebben az oktatóanyagban megértjük a funkcionális programozási paradigma alapelveit és a Java programozási nyelven történő gyakorlást. Kitérünk néhány fejlett funkcionális programozási technikára is.

Ez lehetővé teszi számunkra a funkcionális programozás előnyeinek értékelését is, különösen a Java-ban.

2. Mi a funkcionális programozás

Alapvetően a funkcionális programozás az olyan számítógépes programok írási stílusa, amelyek a számításokat matematikai függvények értékelésének tekintik. Tehát mi a függvény a matematikában?

A függvény olyan kifejezés, amely egy bemeneti halmazt és egy kimeneti halmazt kapcsol össze.

Fontos, hogy egy függvény kimenete csak a bemenetétől függ. Érdekesebb, hogy két vagy több függvényt összeállíthatunk, hogy új funkciót kapjunk.

2.1. Lambda Calculus

Ahhoz, hogy megértsük, miért fontosak ezek a definíciók és a matematikai függvények tulajdonságai a programozásban, kicsit vissza kell mennünk az időben. Az 1930-as években Alonzo Chruch matematikus fejlődött formális rendszer a függvény absztrakción alapuló számítások kifejezésére. A számítás ezen univerzális modellje a Lambda Calculus néven vált ismertté.

A Lambda számításnak óriási hatása volt a programozási nyelvek elméletének fejlesztésére, különös tekintettel a funkcionális programozási nyelvekre. A funkcionális programozási nyelvek általában a lambda számítást valósítják meg.

Mivel a lambda számítás a funkciókompozícióra összpontosít, a funkcionális programozási nyelvek kifejező módon biztosítják a szoftverek összeállítását a funkciók összetételében.

2.2. Programozási paradigmák kategorizálása

Természetesen a funkcionális programozás nem az egyetlen programozási stílus a gyakorlatban. Általánosságban elmondható, hogy a programozási stílusok imperatív és deklaratív programozási paradigmákba sorolhatók:

A Az imperatív megközelítés a programot olyan állítások sorozataként definiálja, amelyek megváltoztatják a program állapotát amíg el nem éri a végső állapotot. Az eljárási programozás az imperatív programozás egyik típusa, ahol eljárásokat vagy szubrutinok segítségével állítunk össze programokat. Az objektum-orientált programozás (OOP) néven ismert egyik népszerű programozási paradigma kiterjeszti az eljárási programozási koncepciókat.

Ezzel szemben a A deklaratív megközelítés kifejezi a számítás logikáját anélkül, hogy leírná annak vezérlési folyamatát az állítások sorrendjét tekintve. Egyszerűen fogalmazva, a deklaratív megközelítés középpontjában az áll, hogy meghatározza, mit kell elérnie a programnak, nem pedig annak, hogyan kellene elérnie. A funkcionális programozás a deklaratív programozási nyelvek részhalmaza.

Ezeknek a kategóriáknak további alkategóriái vannak, és a rendszertan meglehetősen összetetté válik, de ebbe a bemutatóba nem térünk ki.

2.3. Programozási nyelvek kategorizálása

Bármilyen kísérlet a programnyelvek hivatalos kategorizálására ma már önmagában tudományos erőfeszítés! Megpróbáljuk azonban megérteni, hogyan oszlanak meg a programozási nyelvek abból a szempontból, hogy támogatják-e a funkcionális programozást.

A tiszta funkcionális nyelvek, mint a Haskell, csak a funkcionális programokat engedik meg.

Más nyelvek azonban mindkettőt megengedik funkcionális és eljárási programok és tisztátalan funkcionális nyelveknek számítanak. Sok nyelv tartozik ebbe a kategóriába, köztük a Scala, a Kotlin és a Java.

Fontos megérteni, hogy a legnépszerűbb programozási nyelvek manapság általános célú nyelvek, és ezért több programozási paradigmát támogatnak.

3. Alapelvek és fogalmak

Ez a szakasz kitér a funkcionális programozás néhány alapelvére és azok Java alkalmazására. Felhívjuk figyelmét, hogy sok általunk használt funkció nem mindig volt a Java része, és ez az A funkcionális programozás hatékony gyakorlásához tanácsos Java 8 vagy újabb verziót használni.

3.1. Első osztályú és magasabb rendű funkciók

A programnyelv első osztályú funkciókkal rendelkezik, ha első osztályú polgárként kezeli a funkciókat. Alapvetően ez azt jelenti A funkciók támogathatják az összes olyan műveletet, amelyek általában más entitások számára állnak rendelkezésre. Ide tartozik a függvények hozzárendelése a változókhoz, argumentumként más funkciókhoz történő továbbítása és más függvények értékként történő visszaadása.

Ez a tulajdonság lehetővé teszi magasabb rendű függvények definiálását a funkcionális programozásban. A magasabb rendű függvények képesek argumentumként fogadni a függvényt, és ennek eredményeként visszaadni egy függvényt. Ez további funkciókat tesz lehetővé a funkcionális programozásban, mint például a funkciókompozíció és a curry.

Hagyományosan a Java-ban csak olyan konstrukciók segítségével lehetett átadni a függvényeket, mint funkcionális interfészek vagy névtelen belső osztályok. A funkcionális interfészek pontosan egy absztrakt módszerrel rendelkeznek, és más néven Single Abstract Method (SAM) interfészek.

Tegyük fel, hogy egyéni összehasonlítót kell adnunk a Gyűjtemények.rendezés módszer:

Collections.sort (számok, új Comparator () {@Orride public int Compare (Integer n1, Integer n2) {return n1.compareTo (n2);}});

Mint láthatjuk, ez egy fárasztó és részletes technika - minden bizonnyal nem ez ösztönzi a fejlesztőket a funkcionális programozás elfogadására. Szerencsére a Java 8 sokakat hozott a folyamat megkönnyítésére szolgáló új funkciók, például lambda kifejezések, metódus referenciák és előre definiált funkcionális interfészek.

Nézzük meg, hogy a lambda kifejezés hogyan segíthet nekünk ugyanabban a feladatban:

Gyűjtemények.rendezés (számok, (n1, n2) -> n1.compareTo (n2));

Ez mindenképpen tömörebb és érthetőbb. Kérjük, vegye figyelembe, hogy bár ez azt a benyomást keltheti bennünk, hogy a függvényeket első osztályú polgárként használjuk a Java-ban, ez nem így van.

A lambda kifejezések szintaktikai cukora mögött a Java ezeket még mindig funkcionális felületekbe burkolja. Ennélfogva, A Java a lambda kifejezést egyként kezeli Tárgy, ami valójában az első osztályú állampolgár Java-ban.

3.2. Tiszta funkciók

A tiszta funkció meghatározása ezt hangsúlyozza egy tiszta függvénynek csak az argumentumokon alapuló értéket kell visszaadnia, és nem lehet mellékhatása. Most ez teljesen ellentétes lehet a Java összes bevált gyakorlatával.

A Java objektum-orientált nyelvként a kapszulázást javasolja, mint alapvető programozási gyakorlatot. Ösztönzi az objektum belső állapotának elrejtését, és csak az eléréséhez és módosításához szükséges módszerek feltárását. Ezért ezek a módszerek nem szigorúan tiszta funkciók.

Természetesen a beágyazás és más objektumorientált elvek csak ajánlások, és nem kötelezőek a Java-ban. Valójában a fejlesztők nemrégiben elkezdték felismerni a megváltoztathatatlan állapotok és módszerek definiálásának értékét mellékhatások nélkül.

Tegyük fel, hogy meg akarjuk találni az összes számot, amelyet most rendeztünk:

Egész szám (Lista számok) {visszatérési számok.folyam (). Gyűjt (Collectors.summingInt (Egész :: intValue)); }

Ez a módszer csak a kapott argumentumoktól függ, ezért determinisztikus. Sőt, nem okoz mellékhatásokat.

A mellékhatások bármi lehetnek, kivéve a módszer tervezett viselkedését. Például, A mellékhatások olyan egyszerűek lehetnek, mint egy helyi vagy globális állapot frissítése vagy mentés egy adatbázisba az érték visszaadása előtt. A puristák a fakitermelést is mellékhatásként kezelik, de mindannyiunknak meg kell szabnia a saját határait!

Indokolhatjuk azonban, hogy miként kezeljük a jogos mellékhatásokat. Előfordulhat például, hogy valódi okokból az eredményt adatbázisba kell mentenünk. Nos, vannak funkcionális programozási technikák a mellékhatások kezelésére a tiszta funkciók megtartása mellett.

Néhányukat a későbbi szakaszokban tárgyaljuk.

3.3. Állandóság

A változtathatatlanság a funkcionális programozás egyik alapelve, és ez arra a tulajdonságra vonatkozik, amelyet egy entitás nem tud módosítani a példányosítás után. Most funkcionális programozási nyelven ezt a nyelv szintű tervezés támogatja. De a Java-ban meg kell hoznunk a döntést a megváltoztathatatlan adatstruktúrák létrehozásáról.

Kérjük, vegye figyelembe, hogy Maga a Java több beépített változatlan típust kínál, például, Húr. Ez elsősorban biztonsági okokból történik, mivel sokat használunk Húr osztály betöltésében és kulcsokként hash-alapú adatstruktúrákban. Számos más beépített változhatatlan típus létezik, például primitív burkolók és matematikai típusok.

De mi a helyzet a Java-ban létrehozott adatstruktúrákkal? Természetesen alapértelmezés szerint nem változhatatlanok, és néhány változtatást kell végrehajtanunk a változtathatóság elérése érdekében. A a végső kulcsszó az egyik, de nem áll meg itt:

public class ImmutableData {private final String someData; privát döntő AnotherImmutableData anotherImmutableData; public ImmutableData (végleges String someData, végső AnotherImmutableData anotherImmutableData) {this.someData = someData; this.anotherImmutableData = anotherImmutableData; } public String getSomeData () {return someData; } public AnotherImmutableData getAnotherImmutableData () {return anotherImmutableData; }} public class AnotherImmutableData {private final Egész szám someOtherData; public AnotherImmutableData (végleges egész szám egyesData) {this.someOtherData = someData; } public Integer getSomeOtherData () {return someOtherData; }}

Vegye figyelembe, hogy szorgalmasan be kell tartanunk néhány szabályt:

  • A megváltoztathatatlan adatszerkezet minden mezőjének változatlannak kell lennie
  • Ennek vonatkoznia kell az összes beágyazott típusra és gyűjteményre (beleértve az általuk tartalmazottakat is)
  • Szükség esetén egy vagy több konstruktornak kell lennie az inicializáláshoz
  • Csak hozzáférési módszerek lehetnek, esetleg mellékhatások nélkül

Ez az nem könnyű minden alkalommal teljesen rendbe hozni, különösen akkor, ha az adatstruktúrák bonyolódni kezdenek. Számos külső könyvtár megkönnyítheti azonban a Java megváltoztathatatlan adatainak kezelését. Például az Immutables és a Project Lombok használatra kész keretrendszereket kínál a Java megváltoztathatatlan adatstruktúráinak meghatározásához.

3.4. Referenciális átláthatóság

A referenciális átláthatóság talán a funkcionális programozás egyik nehezebben érthető alapelve. A koncepció azonban nagyon egyszerű. Mi hívjon egy kifejezést referenciálisan átlátszónak, ha annak a megfelelő értékre való cseréje nincs hatással a program viselkedésére.

Ez lehetővé teszi néhány hatékony technikát a funkcionális programozásban, mint például a magasabb rendű funkciók és a lusta értékelés. Hogy jobban megértsük ezt, vegyünk egy példát:

public class SimpleData {private Logger logger = Logger.getGlobal (); privát karakterlánc-adatok; public String getData () {logger.log (Level.INFO, "Adatok lekérése a SimpleData számára"); visszatérési adatok; } public SimpleData setData (String data) {logger.log (Level.INFO, "Az adatok beállítása a SimpleData számára szükséges"); ez.adatok = adatok; adja vissza ezt; }}

Ez egy tipikus POJO osztály a Java-ban, de arra vagyunk kíváncsiak, hogy megkapjuk-e ez a referenciális átláthatóságot. Nézzük meg a következő állításokat:

Karakterlánc-adatok = new SimpleData (). SetData ("Baeldung"). GetData (); logger.log (Level.INFO, új SimpleData (). setData ("Baeldung"). getData ()); logger.log (Level.INFO, adatok); logger.log (Level.INFO, "Baeldung");

A három hívás naplózó szemantikailag egyenértékűek, de referenciálisan nem átláthatók. Az első hívás referenciálisan nem átlátszó, mivel mellékhatást vált ki. Ha ezt a hívást lecseréljük az értékére, mint a harmadik hívásban, akkor hiányzik a napló.

A második hívás szintén nem viszonylag átlátszó, mivel SimpleData megváltoztatható. Felhívás data.setData bárhol a programban megnehezítené annak helyettesítését az értékével.

Úgyhogy alapvetően, a referenciális átláthatóság érdekében funkcióinknak tisztának és megváltoztathatatlannak kell lenniük. Ez a két előfeltétel, amelyet már korábban tárgyaltunk. A referenciális átláthatóság érdekes eredményeként kontextusmentes kódot állítunk elő. Más szavakkal, bármilyen sorrendben és összefüggésben végrehajthatjuk őket, ami különböző optimalizálási lehetőségekhez vezet.

4. Funkcionális programozási technikák

A korábban tárgyalt funkcionális programozási elvek lehetővé teszik számunkra, hogy több technikát használjunk a funkcionális programozás előnyeinek kihasználására. Ebben a szakaszban bemutatjuk ezeket a népszerű technikákat, és megértjük, hogyan tudjuk ezeket megvalósítani a Java-ban.

4.1. Funkcióösszetétel

Funkcióösszetétel komplex függvények összeállítására utal egyszerűbb függvények kombinálásával. Ez elsősorban a Java-ban érhető el funkcionális interfészek használatával, amelyek valójában a lambda-kifejezések és a módszer-referenciák céltípusai.

Általában bármely interfész egyetlen elvont módszerrel funkcionális interfészként szolgálhat. Ezért meglehetősen egyszerűen meghatározhatunk egy funkcionális interfészt. A Java 8 azonban alapértelmezés szerint sok funkcionális interfészt biztosít számunkra a csomag különböző használata esetén java.util.function.

Ezen funkcionális interfészek közül sok támogatást nyújt a funkciók összetételéhez alapértelmezett és statikus mód. Válasszuk a Funkció felületen, hogy ezt jobban megértsük. Funkció egy egyszerű és általános funkcionális interfész, amely elfogad egy argumentumot és eredményt ad.

Két alapértelmezett módszert is kínál, összeállít és és akkor, amely segít a funkciók összetételében:

Funkciónapló = (érték) -> Math.log (érték); Funkció sqrt = (érték) -> Math.sqrt (érték); Funkció logThenSqrt = sqrt.compose (log); logger.log (Level.INFO, String.valueOf (logThenSqrt.apply (3.14))); // Kimenet: 1.06 Funkció sqrtThenLog = sqrt.andThen (log); logger.log (Level.INFO, String.valueOf (sqrtThenLog.apply (3.14))); // Kimenet: 0.57

Mindkét módszer lehetővé teszi számunkra, hogy több függvényt egyetlen funkcióba állítsunk össze, de különböző szemantikát kínálnak. Míg összeállít először alkalmazza az argumentumban átadott függvényt, majd azt a függvényt, amelyre meghívták, és akkor ugyanezt teszi fordítva.

Számos más funkcionális interfész rendelkezik érdekes módszerek a funkciókompozícióban, például az alapértelmezett módszerek és, vagy, és tagadni ban,-ben Állítmány felület. Míg ezek a funkcionális felületek egyetlen érvet fogadnak el, vannak kétarányos specializációk, mint például BiFunction és BiPredicate.

4.2. Monádok

A funkcionális programozási koncepciók közül sok a kategóriaelméletből származik, vagyis a matematika függvényeinek általános elmélete. Számos kategória-fogalmat mutat be, mint például a functorok és a természetes átalakulások. Számunkra csak azt kell tudni, hogy ez az alapja a monádok használatának a funkcionális programozásban.

Formálisan a monád egy absztrakció, amely lehetővé teszi a programok általános strukturálását. Tehát alapvetően egy monád lehetővé teszi számunkra, hogy becsomagoljunk egy értéket, alkalmazzunk egy transzformációs halmazt, és az összes alkalmazott transzformációval visszakapjuk az értéket. Természetesen három törvényt kell betartania minden monádnak - bal identitás, jobb identitás és asszociativitás -, de nem térünk ki a részletekre.

A Java-ban van néhány monád, amelyeket elég gyakran használunk, például Választható és Folyam:

Opcionális.f (2) .flatMap (f -> Opcionális.of (3) .flatMap (s -> Opcionális.f (f + s)))

Most miért hívunk Választható monád? Itt, Választható lehetővé teszi számunkra, hogy becsüljünk egy értéket a módszer segítségével nak,-nek és alkalmazzon egy sor transzformációt. A másik becsomagolt érték hozzáadásának átalakítását alkalmazzuk a módszerrel flatMap.

Ha akarjuk, ezt megmutathatjuk Választható a monádok három törvényét követi. A kritikusok azonban gyorsan rámutatnak, hogy an Választható bizonyos körülmények között megszegi a monád törvényeket. De a legtöbb gyakorlati helyzetben elég jónak kell lennie számunkra.

Ha megértjük a monádok alapjait, hamarosan rájövünk, hogy sok más példa is van a Java-ban, például Folyam és CompletableFuture. Segítenek különböző célkitűzések elérésében, de mindegyikük szabványos összetételű, amelyben a kontextus manipulációját vagy átalakítását kezelik.

Természetesen, meghatározhatjuk saját monádtípusainkat a Java-ban a különböző célok elérése érdekében mint naplómonád, jelentésmonád vagy auditmonád. Emlékszel, hogyan beszéltük meg a mellékhatások kezelését a funkcionális programozásban? Nos, amint látszik, a monád az egyik funkcionális programozási technika ennek elérésére.

4.3. Curry

A curry egy matematikai a több argumentumot tartalmazó függvény átalakításának technikája egyetlen argumentumot felvevő függvénysorozattá. De miért van szükség rájuk a funkcionális programozásban? Erőteljes kompozíciós technikát ad nekünk, ahol nem kell egy függvényt minden argumentumával meghívnunk.

Sőt, egy curried függvény csak akkor éri el hatását, ha megkapja az összes érvet.

A tiszta funkcionális programozási nyelvekben, mint a Haskell, a curry-t jól támogatják. Valójában az összes funkció alapértelmezés szerint curry. A Java-ban azonban ez nem ilyen egyértelmű:

Funkció súly = tömeg -> gravitáció -> tömeg * gravitáció; Funkció weightOnEarth = súly.alkalmazás (9,81); logger.log (Level.INFO, "Súlyom a Földön:" + weightOnEarth.apply (60.0)); Funkció weightOnMars = súly.alkalmazás (3,75); logger.log (Level.INFO, "Súlyom a Marson:" + weightOnMars.apply (60.0));

Itt meghatároztunk egy funkciót a bolygónk súlyának kiszámításához. Míg tömegünk változatlan marad, a gravitáció azon a bolygónként változik, ahol vagyunk. Mi részben alkalmazhatja a függvényt azzal, hogy csak a gravitációt adja át egy adott bolygó függvényének meghatározásához. Sőt, átadhatjuk ezt a részben alkalmazott függvényt argumentumként vagy visszatérési értékként az önkényes összetételhez.

Curry a nyelvtől függ, hogy két alapvető tulajdonságot biztosítson: lambda kifejezéseket és bezárásokat. A lambda kifejezések anonim funkciók, amelyek segítenek abban, hogy a kódot adatként kezeljük. Korábban láthattuk, hogyan lehet ezeket funkcionális interfészek segítségével megvalósítani.

Most egy lambda kifejezés bezárulhat lexikális hatókörébe, amelyet bezárásként határozunk meg. Lássunk egy példát:

privát statikus Funkció weightOnEarth () {végső kettős gravitáció = 9,81; visszatérő tömeg -> tömeg * gravitáció; }

Kérjük, vegye figyelembe, hogy a lambda kifejezés, amelyet a fenti módszerrel adunk vissza, függ a becsatoló változótól, amelyet bezárásnak hívunk. Más funkcionális programozási nyelvektől eltérően A Java korlátozza, hogy a mellékelő hatókörnek véglegesnek vagy ténylegesen véglegesnek kell lennie.

Érdekes eredményként a curry lehetővé teszi tetszőleges aritású funkcionális felület létrehozását a Java-ban.

4.4. Rekurzió

A rekurzió egy másik hatékony technika a funkcionális programozásban, amely lehetővé teszi számunkra, hogy egy problémát kisebb darabokra bontsunk. A rekurzió legfőbb előnye, hogy segít kiküszöbölni a mellékhatásokat, ami jellemző minden imperatív stílushurkolásra.

Lássuk, hogyan számoljuk ki a szám faktoriáját rekurzióval:

Egész tényező (Egész szám) {return (szám == 1)? 1: szám * faktoriális (szám - 1); }

Itt ugyanazt a függvényt rekurzív módon hívjuk meg, amíg el nem érjük az alapesetet, majd elkezdjük kiszámolni az eredményünket.Figyelje meg, hogy a rekurzív hívást kezdeményezzük, mielőtt kiszámoljuk az eredményt minden lépésnél, vagy szavakkal a számítás elején. Ennélfogva, ezt a rekurzióstílust fejrekurziónak is nevezik.

Az ilyen típusú rekurzió hátránya, hogy minden lépésnek meg kell tartania az összes előző lépés állapotát, amíg el nem érjük az alapesetet. Kis szám esetén ez valójában nem jelent problémát, de az állam nagy számoknál való tartása nem hatékony.

Megoldás a a farokrekurziónak nevezett rekurzió kissé eltérő megvalósítása. Itt biztosítjuk, hogy a rekurzív hívás legyen az utolsó hívás, amelyet a funkció végrehajt. Nézzük meg, hogyan írhatjuk át a fenti függvényt a farokrekurzió használatához:

Egész tényező (Egész szám, Egész eredmény) {return (szám == 1)? eredmény: faktoriális (szám - 1, eredmény * szám); }

Figyelje meg az akkumulátor használatát a függvényben, így nincs szükség az állapot megtartására a rekurzió minden lépésében. Ennek a stílusnak az igazi előnye a fordító optimalizálásainak kihasználása, ahol a fordító eldöntheti, hogy elengedi-e az aktuális függvény veremkeretét, ezt a technikát farokhívás megszüntetésnek nevezik.

Míg sok olyan nyelv, mint a Scala, támogatja a farokhívás megszüntetését, a Java még mindig nem támogatja ezt. Ez a Java elmaradásának része, és valószínűleg valamilyen formában megjelenik a Project Loom keretében javasolt nagyobb változtatások részeként.

5. Miért fontos a funkcionális programozás?

Az eddigi oktatóanyag átnézése után csodálkoznunk kell azon, hogy miért is akarunk ennyi erőfeszítést tenni. Valaki Java háttérből származik, a funkcionális programozás által igényelt eltolódás nem triviális. Tehát valóban ígéretes előnyökkel kell rendelkeznie a funkcionális programozás Java alkalmazásában.

A legnagyobb előnye annak, hogy bármilyen nyelven, beleértve a Java-t is, a funkcionális programozást alkalmazza tiszta funkciók és megváltoztathatatlan állapotok. Ha utólag gondolkodunk, akkor a programozási kihívások nagy része a mellékhatásokban és a mutábilis állapotban gyökerezik így vagy úgy. Egyszerűen megszabadulni tőlük megkönnyíti programunk olvasását, okosítását, tesztelését és karbantartását.

A deklaratív programozás mint olyan, nagyon tömör és olvasható programokhoz vezet. A funkcionális programozás, mivel a deklaratív programozás részhalmaza, számos konstrukciót kínál, mint például magasabb rendű függvények, függvényösszetétel és függvényláncolás. Gondoljon azokra az előnyökre, amelyeket a Stream API hozott a Java 8-ba az adatkezelések kezelésében.

De ne érjen kísértés, hogy váltson át, csak akkor, ha teljesen készen áll. Felhívjuk figyelmét, hogy a funkcionális programozás nem egy egyszerű tervezési minta, amelyet azonnal felhasználhatunk és előnyökkel szolgálhatunk. A funkcionális programozás az sokkal inkább annak megváltoztatása, hogy miért okoskodunk a problémákról és azok megoldásairól és hogyan kell felépíteni az algoritmust.

Tehát, mielőtt elkezdenénk használni a funkcionális programozást, ki kell képeznünk magunkat, hogy gondolkodjunk programjainkon a funkciók tekintetében.

6. A Java alkalmas-e?

Noha nehéz tagadni a funkcionális programozási előnyöket, nem kérdezhetjük meg, hogy a Java megfelelő választás-e rá. Történelmileg A Java általános célú programozási nyelvként fejlődött, amely alkalmasabb az objektum-orientált programozásra. Még a Java 8 előtti funkcionális programozásra való gondolkodás is unalmas volt! De a dolgok határozottan megváltoztak a Java 8 után.

Maga a tény a Java-ban nincsenek igazi függvénytípusok ellentmond a funkcionális programozás alapelveinek. A funkcionális interfészek a lambda kifejezések álcájában nagyrészt, legalábbis szintaktikailag, ezt pótolják. Aztán az a tény, hogy a Java-típusok eleve módosíthatók és annyi kazánt kell írnunk, hogy megváltoztathatatlan típusokat hozzunk létre, az nem segít.

Más dolgokat várunk el egy funkcionális programozási nyelvtől, amelyek hiányoznak vagy nehezek a Java-ban. Például, az Java alapértelmezett értékelési stratégiája lelkes. De a lusta értékelés hatékonyabb és ajánlottabb módja a funkcionális programozásnak.

A Java-ban még mindig elérhetünk lusta értékelést a kezelő rövidzárlatának és funkcionális interfészeinek használatával, de ez jobban érintett.

A lista minden bizonnyal nem teljes, és tartalmazhat generikus támogatást a típus törlésével, a farokhívás optimalizálásához hiányzó támogatást és egyéb dolgokat. Tág elképzelést kapunk azonban. A Java határozottan nem alkalmas egy program nulláról történő elindítására a funkcionális programozásban.

De mi van akkor, ha már van egy Java-val írt programunk, valószínűleg objektum-orientált programozással? Semmi sem akadályozza meg bennünket abban, hogy a funkcionális programozás előnyeit élvezzük, különös tekintettel a Java 8-ra.

Itt rejlik a funkcionális programozás legtöbb előnye egy Java fejlesztő számára. Az objektum-orientált programozás és a funkcionális programozás előnyeinek kombinációja hosszú utat jelenthet.

7. Következtetés

Ebben az oktatóanyagban áttekintettük a funkcionális programozás alapjait. Kitértünk az alapelvekre és arra, hogy miként alkalmazhatjuk őket Java-ban. A funkcionális programozás néhány népszerű technikáját megvitattuk Java példákkal.

Végül kitértünk a funkcionális programozás alkalmazásának néhány előnyére, és megválaszoltuk, hogy a Java alkalmas-e ugyanarra.

A cikk forráskódja elérhető a GitHubon.