Функционално програмиране на Java с примери

Ако сте разработчик на Java, сигурен съм, че поне веднъж сте виждали код, подобен на горния фрагмент. Кодът в горния фрагмент е пример за внедряване на парадигма на функционално програмиране в Java, която ще филтрира и трансформира List<String> в заявката в друг List<String>.

В тази статия ще пиша за това как да пиша код с помощта на API на Java за функционално програмиране. В крайна сметка ще напишем наш собствен поток API, за да можем да разберем как да приложим стил на функционално програмиране в Java.

Функционално програмиране в Java

Функционалното програмиране в Java съществува от дълго време. Когато Oracle пусна Java 8 през 2014 г., те въведоха „ламбда израз“, който беше основната функция за функционално програмиране в Java.

Нека видим пример за разликата между използването на поредица от императивни изрази и използването на функционален стил в Java.

Както виждаме, въпреки че и двете парчета код постигат един и същ резултат, разликата е значителна. Кодът на императивната декларация има много фигурни скоби и е много по-дълъг, което го прави по-труден за четене в сравнение с кода на функционалния стил.

Анотация на функционалния интерфейс

За да разберем как работи функционалното програмиране в Java, първо ще трябва да разгледаме анотацията, включена в Java 8 SDK, @FunctionalInterface. Можем да го разгледаме на сайта за документация на Java API.

От документацията на API можем да видим, че поведението на анотация на функционален интерфейс в Java е:

  • В него има точно един абстрактен метод.
  • Може да има повече от един метод, стига да има само един абстрактен метод.
  • Можем да го добавим само към тип Interface.
  • Можем да създадем функционалния интерфейс с ламбда израз, препратки към методи или препратки към конструктор.
  • Не е необходимо да дефинираме @FunctionalInterface, защото компилаторът ще третира всеки интерфейс, отговарящ на определението за функционален интерфейс, като функционален интерфейс.

Създаване на функционален интерфейсен клас

След като знаем какво представлява функционалният интерфейс, можем да го създадем сами.

Нека първо създадем модел, наречен Person.

За функционалния интерфейс ще създадем PersonFunctionalInterface class.

Имайте предвид, че има два метода в интерфейса, но тъй като има само един абстрактен метод, PersonFunctionalInterfaceclass е валиден като функционален интерфейс.

Но да предположим, че дефинираме повече от един абстрактен метод, така:

Ще генерира грешка:

Използване на функционален интерфейс

Анонимен клас

Нека първо научим за анонимния клас. „Документацията“ на Java казва, че:

„Анонимните класове ви позволяват да направите кода си по-сбит. Те ви позволяват да декларирате и инстанциирате клас едновременно. Те са като локални класове, с изключение на това, че нямат име. Използвайте ги, ако трябва да използвате локален клас само веднъж.

По принцип с анонимен клас не е нужно да дефинираме клас, който имплементира създадения от нас интерфейс. Можем да създадем клас без име и да го съхраним в променлива.

Нека да декларираме анонимен клас като пример.

Това, което направихме тук, е, че създадохме анонимен клас с тип PersonFunctionalInterface и име anonClassExample.

Ние отменяме createPerson абстрактния метод, така че когато извикаме метода, той ще върне нов обект Person с име.

Когато извикахме anonClassExample.createPerson(“Hello, World”), ние просто създадохме нов обект Person с име „Hello, World“.

Създаване на анонимен клас с функционален интерфейс

Можем да започнем да създаваме анонимния клас на PersonFunctionalinterface за функционалния интерфейс, който направихме.

Току-що внедрихме функционалния интерфейс!

В кода по-горе създадохме три анонимни класа по различни начини. Не забравяйте, че анонимният клас има поведението, че можем да създадем функционален интерфейс с ламбда израз, препратки към методи или препратки към конструктор.

За да сме сигурни, че сме създали анонимни класове, които се държат еднакво, ние отстояваме всеки метод в интерфейса.

Вграден функционален интерфейс в Java 8

Java 8 има много вградени функционални интерфейсни класове в пакета java.util.function, които можем да видим в неговата документация.

В тази статия ще обясня само четири от най-често използваните функционални интерфейси, но ако се интересувате от повече, можете да го прочетете в документацията за Java API, отбелязана по-горе.

  • Consumer<T>: Функционален интерфейс, който приема обект и не връща нищо.
  • Producer<T>: Функционален интерфейс, който не приема нищо и връща обект.
  • Predicate<T>: Функционален интерфейс, който приема обект и връща булево значение.
  • Function<T, R>: Функционален интерфейс, който приема обект и връща друг обект.

Обща употреба

Ако сте разработвали с Java много, вероятно вече сте се запознали с концепцията за функционален интерфейс.

Поток и опционален API

Java Stream API използва много функционални интерфейси, както можем да видим в кода по-долу.

Методът filter има параметър Predicate<T> функционален интерфейс. Както виждаме, методът приема String и произвежда boolean.

Методът map използва Function<T, R> като свой параметър. Той приема String и също връща String.

Методът forEach в Stream и методът ifPresent в Optional приемат Consumer<T>, като приемат String и не връщат нищо.

Реактивна библиотека

И двете най-популярни Java Reactive библиотеки, RxJava и Reactor, са базирани на Java 8 Streams API, което означава, че те също използват функционални интерфейси в кода си.

Ако погледнем документацията на Reactor’s Flux API и документацията на RxJava’s Observable API, можем да видим, че много от техните методи приемат функционален интерфейс.

Създаване на наш собствен поток API

След като вече знаем как да създадем и използваме функционален интерфейс, нека опитаме да създадем наш собствен API за стрийминг, за да можем да разберем как можем да внедрим функционалния интерфейс.

Разбира се, нашият API за стрийминг е много по-опростен от Java.

И тестов клас:

Добре, нека обсъдим методите един по един.

Конструктор

Направихме два конструктора, един конструктор, имитиращ Stream.of() API и един конструктор за преобразуване на List<T> в SimpleStream<T>.

Филтър

В този метод ние приемаме Predicate<T> като параметър, тъй като Predicate<T> има абстрактен параметър с име test, който приема обект и създава булево значение.

Нека да разгледаме тестовия клас, където написахме:

Това означава, че написахме анонимен клас, прилагащ Predicate<T>:

Така че в класа SimpleStream<T> можем да видим филтърния метод като:

Карта

В метода map ние приемаме Function<T, R> като негов параметър, което означава, че методът map ще приеме функционален интерфейс, който приема обект и също така създава обект.

Написахме следното в тестовия клас:

Това е същото като създаването на анонимен клас, прилагащ Function<T, R>:

И в класа SimpleStream<T> можем да го видим така:

за всеки

Методът forEach приема Consumer<T> като свой параметър, което означава, че ще приеме обект и няма да върне нищо.

Написахме следното в тестовия клас:

Това означава създаване на анонимен клас, прилагащ Consumer<T>:

В SimpleStream<T> можем да видим метода forEach, както е показано по-долу:

Заключение

С пускането на Java 8 през 2014 г. можем да използваме стил на функционално програмиране в Java. Използването на стил на функционално програмиране в Java има много предимства, едно от които прави вашия код по-кратък и по-четлив. С предимствата, които предоставя, познаването на внедряването на функционално програмиране в Java, ако сте Java разработчик, е задължително!

Благодаря, че прочетохте тази статия!

Можете да намерите хранилището на GitHub, използвано за тази статия тук:



Ресурси

  1. https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
  2. https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
  3. https://www.amitph.com/java-method-and-constructor-reference/#:~:text=Constructor%20Reference%20is%20used%20to,assign%20to%20a%20target%20type.
  4. https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
  5. https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
  6. http://reactivex.io/RxJava/javadoc/
  7. https://projectreactor.io/docs/core/release/api/