Функциональное программирование в Java
Сейчас появляются новые модные языки использующие парадигму функционального программирования. Тем не менее, в обычной Java
можно использовать функции для описания поведения объектов. Причём делать это можно полностью в рамках синтаксиса Java.
Я опубликовал Java-библиотеку позволяющую связывать (binding) объекты через функции (см. https://code.google.com/p/tee-binding/ )
Описание классов
public class It‹E›
— основной класс, содержит ссылку на объект любого типа и обновляет все связи при изменении значений в одном из экземпляров. Пример
It‹String› a1 = new It‹String›().value("A"); It‹String› a2 = new It‹String›().value("B"); System.out.println("a1: "+a1.value()+", a2: "+a2.value()); a1.bind(a2); System.out.println("a1: "+a1.value()+", a2: "+a2.value()); a1.value("C"); System.out.println("a1: "+a1.value()+", a2: "+a2.value()); a2.value("D"); System.out.println("a1: "+a1.value()+", a2: "+a2.value());
a1: A, a2: B a1: B, a2: B a1: C, a2: C a1: D, a2: D
Класссы Number, Note, Toggle являются производными класса It для хранения значений конкретного типа (соответственно для Double, String и Boolean) и содержат методы задания связывания с использованием функций. Пример:
Numeric c = new Numeric().value(0); Numeric f = c.multiply(9.0).divide(5.0).plus(32.0); System.out.println("f: " + f.value() + ", c: " + c.value()); System.out.println("/let f = 100 "); f.value(100); System.out.println("f: " + f.value() + ", c: " + c.value()); System.out.println("/let c = 100 "); c.value(100); System.out.println("f: " + f.value() + ", c: " + c.value());
f: 32.0, c: 0.0 /let f = 100 f: 100.0, c: 37.77777777777778 /let c = 100 f: 212.0, c: 100.0
как видно, это функция конвертации температуры из шкалы Цельсия в шкалу Фаренгейта (F’ = C’ * 9 / 5 + 32). Из определения переменной
Numeric f = c.multiply(9.0).divide(5.0).plus(32.0);
это вполне очевидно. Также можно отметить что связывание через функцию является двунаправленным.
Примечание: псевдооператоры функции вычисляются последовательно без учёта приориетета операций.
Для более сложных случаев можно использовать класс Fork. Он позволяет использовать в связывании условия, пример:
System.out.println("/n = -10"); Numeric n = new Numeric().value(-10); Note r = new Note().bind(new Fork‹String›() .condition(new Toggle().less(n, -5)) .then("Frost") .otherwise(new Fork‹String›() .condition(new Toggle().less(n, +15)) .then("Cold") .otherwise(new Fork‹String›() .condition(new Toggle().less(n, +30)) .then("Warm") .otherwise("Hot") ))); System.out.println(r.value()); System.out.println("/let n = +10"); n.value(10); System.out.println(r.value()); System.out.println("/let n = +20"); n.value(20); System.out.println(r.value()); System.out.println("/let n = +40"); n.value(40); System.out.println(r.value());
/n = -10 Frost /let n = +10 Cold /let n = +20 Warm /let n = +40 Hot
Запись условия вполне наглядна, в зависимости от значения переменной n, в переменную r заносится текст Frost, Cold, Warm или Hot.
Применение библиотеки
К сожалению, связывание и функции нельзя использовать непосредственно. Рассмотрим модификации необходимые для применения связывания в Swing:
class BindableLabel extends JLabel < private Note bindableValue = new Note().value("").afterChange(new Task() < @Override public void job() < if (bindableValue != null) < setText(bindableValue.value()); >> >); public Note bindableValue() < return bindableValue; >public BindableLabel() < super(); >>
это класс расширяющий стандартный JLabel. Он позволяет обновлять связывать текст надписи с переменно имеющий тип Note.
Для редактируемых Swing-компонентов также придётся добавить ChangeListener. Пример определения поведения компонентов из формы на скриншоте:
void bindComponents() < Numeric celsius = new Numeric().value(0); Numeric fahrenheit = celsius.multiply(9.0).divide(5.0).plus(32.0); fahrenheitSlider.bindableValue().bind(fahrenheit); fahrenheitSpinner.bindableValue().bind(fahrenheit); celsiusSlider.bindableValue().bind(celsius); celsiusSpinner.bindableValue().bind(celsius);
— как видно, это занимает всего несколько строк и при редактировании любого значения в форме (или перемещения ползунка слайдера) остальные компоненты мгновенно обновляют своё состояние.
Функциональное программирование: что это такое, преимущества и недостатки
Функциональное программирование на сегодняшний день является одним из приоритетных направлений развития кода. Оно кардинально отличается от императивной, объектно-ориентированной парадигмы. К примеру, если в императивном подходе используются инструкции, то в функциональном – функции, набор правил, которые необходимо выполнять без строгой последовательности действий.
Haskell, Erlang, Clojur – яркие представители ФП. На другом берегу такие мастодонты, как C++, Java. Конечно, принципы первой группы языков применимы далеко не везде, однако в своих сферах они практически незаменимы. О том, что такое функциональное программирование, вы узнаете из нашего материала.
Что такое функциональные системы программирования
Практически каждый программист первым делом изучал объектно-ориентированную методологию разработок. Обычно вхождение в эту специальность предполагает знакомство с языками Java или C++, а в лучшем случае Ruby, Python или C#. Такой разработчик уже точно будет иметь представление о классах, объектах и т.д. Но вот основы функционального программирования, скорее всего, ему еще не будут знакомы. Эта парадигма существенно отличается не только от объектно-ориентированного подхода, но и от других методологий (процедурная, прототипно-ориентированная и др.).
Функциональное программирование становится все более востребованным. Это обусловлено рядом факторов. Нельзя сказать, что это новая парадигма. Так, еще в девяностых годах появился язык программирования Haskell, который и сейчас активно используется для ФП. В эту же категорию могут быть отнесены также Erlang, Scala, Clojure. Все подобные языки программирования объединяет одно очень важное преимущество. С их помощью можно писать конкурентные программные продукты, поэтому при их использовании отпадают такие проблемы, как взаимные блокировки и потокобезопасность.
Суть функциональной парадигмы программирования заключается в том, что разработчик должен задавать не последовательность требуемых команд, а описать принцип их взаимодействия с подпрограммами. Это напоминает работу в ООП, но в этом случае реализация осуществляется на уровне всего программного продукта.
Узнай, какие ИТ - профессии
входят в ТОП-30 с доходом
от 210 000 ₽/мес
Павел Симонов
Исполнительный директор Geekbrains
Команда GeekBrains совместно с международными специалистами по развитию карьеры подготовили материалы, которые помогут вам начать путь к профессии мечты.
Подборка содержит только самые востребованные и высокооплачиваемые специальности и направления в IT-сфере. 86% наших учеников с помощью данных материалов определились с карьерной целью на ближайшее будущее!
Скачивайте и используйте уже сегодня:

Павел Симонов
Исполнительный директор Geekbrains
Топ-30 самых востребованных и высокооплачиваемых профессий 2023
Поможет разобраться в актуальной ситуации на рынке труда
Подборка 50+ бесплатных нейросетей для упрощения работы и увеличения заработка
Только проверенные нейросети с доступом из России и свободным использованием
ТОП-100 площадок для поиска работы от GeekBrains
Список проверенных ресурсов реальных вакансий с доходом от 210 000 ₽
Получить подборку бесплатно
Уже скачали 25504
К примеру, в объектно-ориентированном программировании разработчик должен задать объекты и описать алгоритмы их взаимодействия друг с другом, но, при этом, есть возможность для написания кода, не связанного с ними. Такой код выглядит обособленным и оказывает влияние на работу всей программы. Он направляет некоторые объекты для взаимодействия между собой, осуществляет обработку определенных результатов и т.д.
Функциональная парадигма в этом плане продвинулась глубже. При таком программировании код описывает правила работы с данными. Разработчик лишь формирует необходимые условия, а код уже непосредственно определяет их практическое применение.
Сравнивая процедурное и функциональное программирование, можно отметить совпадения только в наличии команд, которые выполняет язык. Все остальные аспекты этих парадигм отличаются.
Понятие функционального программирования связано с парадигмами, при которых приложения разрабатываются путем последовательного использования функций. А при процедурном подходе применяются инструкции.
Сравнение императивного и функционального программирования
Можно решить, что термин «функциональный стиль программирования» говорит о функциях.
Но это не так. Функции представлены в различных парадигмах. Но в отношении функционального программирования отличие от императивного заключается в самом подходе к написанию приложений.
Для наглядности рассмотрим ситуацию с открытием кафе. Здесь предусмотрено наличие таких типов работников, как повара и администраторы.
Поварам необходимо предоставить пошаговую инструкцию по приготовлению каждого блюда. К примеру:
- Наполнить кастрюлю водой.
- Разместить кастрюлю на плите и установить определенную температуру нагрева.
- Посолить воду в кастрюле с использованием нужного количества соли.
- При приготовлении 10 порций, нужно использовать 1 свеклу, для 20 порций – 2 две свёклы и т.д.
- Почистить всю отобранную свеклу.
- …
Что такое функциональное программирование
В программировании есть два больших подхода — императивное и функциональное. Они существенно отличаются логикой работы, ещё и создают путаницу в названиях. Сейчас объясним.
Функциональное — это про функции?
❌ Нет. Функциональное — это не про функции. Функции есть почти в любых языках программирования: и в функциональных, и в императивных. Отличие функционального программирования от императивного — в общем подходе.
Метафора: инструкция или книга правил
Представьте, что вы открываете кафе-столовую. Сейчас у вас там два типа сотрудников: повара и администраторы.
Для поваров вы пишете чёткие пошаговые инструкции для каждого блюда. Например:
- Налить воды в кастрюлю
- Поставить кастрюлю с водой на огонь
- Добавить в кастрюлю с водой столько-то соли
- Если нужно приготовить 10 порций, взять одну свёклу. Если нужно приготовить 20 порций, взять две свёклы.
- Почистить всю свёклу, которую вы взяли
- …
Повар должен следовать этим инструкциям ровно в той последовательности, в которой вы их написали. Нельзя сначала почистить свёклу, а потом взять её. Нельзя посолить кастрюлю, в которой нет воды. Порядок действий важен и определяется вами. Это пример императивного программирования. Вы повелеваете исполнителем. Можно сказать, что исполнители выполняют ваши задания.
Для администратора вы пишете не инструкцию, а как бы книгу правил:
- У нас нельзя со своим. Если гости пришли со своим, то сделать им замечание такое-то.
- В зале должно быть чисто. Если в зале грязно, вызвать уборщика.
- Если образовалась очередь, открыть дополнительную кассу.
Это тоже команды, но исполнять их администратор будет не в этой последовательности, а в любой на своё усмотрение. Можно сказать, что задача этого человека — исполнять функции администратора, и мы описали правила, по которым эти функции исполнять. Это пример функционального программирования.

❌ Программисты, не бомбите
Конечно же, это упрощено для понимания. Вы сами попробуйте это нормально объяснить (можно прямо в комментах).
Императивное программирование
Примеры языков: C, С++, Go, Pascal, Java, Python, Ruby
Императивное программирование устроено так:
В языке есть команды, которые этот язык может выполнять. Эти команды можно собрать в подпрограммы, чтобы автоматизировать некоторые однотипные вычисления. В каком порядке записаны команды внутри подпрограммы, в том же порядке они и будут выполняться.
Есть переменные, которые могут хранить данные и изменяться во время работы программы. Переменная — это ячейка для данных. Мы можем создать переменную нужного нам типа, положить туда какое-то значение, а потом поменять его на другое.
Если подпрограмме на вход подать какое-то значение, то результат будет зависеть не только от исходных данных, но и от других переменных. Например, у нас есть функция, которая возвращает размер скидки при покупке в онлайн-магазине. Мы добавляем в корзину товар стоимостью 1000 ₽, а функция должна нам вернуть размер получившейся скидки. Но если скидка зависит от дня недели, то функция сначала проверит, какой сегодня день, потом посмотрит по таблице, какая сегодня скидка.
Получается, что в разные дни функция получает на вход 1000 ₽, но возвращает разные значения — так работает императивное программирование, когда всё зависит от других переменных.
Последовательность выполнения подпрограмм регулируется программистом. Он задаёт нужные условия, по которым движется программа. Вся логика полностью продумывается программистом — как он скажет, так и будет. Это значит, что разработчик может точно предсказать, в какой момент какой кусок кода выполнится — код получается предсказуемым, с понятной логикой работы.
Если у нас код, который считает скидку, должен вызываться только при финальном оформлении заказа, то он выполнится именно в этот момент. Он не посчитает скидку заранее и не пропустит момент оформления.
Суть императивного программирования в том, что программист описывает чёткие шаги, которые должны привести код к нужной цели.
Звучит логично, и большинство программистов привыкли именно к такому поведению кода. Но функциональное программирование работает совершенно иначе.
Функциональное программирование
Примеры языков: Haskell, Lisp, Erlang, Clojure, F#
Смысл функционального программирования в том, что мы задаём не последовательность нужных нам команд, а описываем взаимодействие между ними и подпрограммами. Это похоже на то, как работают объекты в объектно-ориентированном программировании, только здесь это реализуется на уровне всей программы.
Например, в ООП нужно задать объекты и правила их взаимодействия между собой, но также можно и написать просто код, который не привязан к объектам. Он как бы стоит в стороне и влияет на работу программы в целом — отправляет одни объекты взаимодействовать с другими, обрабатывает какие-то результаты и так далее.
Функциональное программирование здесь идёт ещё дальше. В нём весь код — это правила работы с данными. Вы просто задаёте нужные правила, а код сам разбирается, как их применять.
Если мы сравним принципы функционального подхода с императивным, то единственное, что совпадёт, — и там, и там есть команды, которые язык может выполнять. Всё остальное — разное.
Команды можно собирать в подпрограммы, но их последовательность не имеет значения. Нет разницы, в каком порядке вы напишете подпрограммы — это же просто правила, а правила применяются тогда, когда нужно, а не когда про них сказали.
Переменных нет. Вернее, они есть, но не в том виде, к которому мы привыкли. В функциональном языке мы можем объявить переменную только один раз, и после этого значение переменной измениться не может. Это как константы — записали и всё, теперь можно только прочитать. Сами же промежуточные результаты хранятся в функциях — обратившись к нужной, вы всегда получите искомый результат.
Функции всегда возвращают одно и то же значение, если на вход поступают одни и те же данные. Если в прошлом примере мы отдавали в функцию сумму в 1000 ₽, а на выходе получали скидку в зависимости от дня недели, то в функциональном программировании если функция получит в качестве параметра 1000 ₽, то она всегда вернёт одну и ту же скидку независимо от других переменных.
Можно провести аналогию с математикой и синусами: синус 90 градусов всегда равен единице, в какой бы момент мы его ни посчитали или какие бы углы у нас ещё ни были в задаче. То же самое и здесь — всё предсказуемо и зависит только от входных параметров.
Последовательность выполнения подпрограмм определяет сам код и компилятор, а не программист. Каждая команда — это какое-то правило, поэтому нет разницы, когда мы запишем это правило, в начале или в конце кода. Главное, чтобы у нас это правило было, а компилятор сам разберётся, в какой момент его применять.
В русском языке всё работает точно так же: есть правила правописания и грамматики. Нам неважно, в каком порядке мы их изучили, главное — чтобы мы их вовремя применяли при написании текста или в устной речи. Например, мы можем сначала пройти правило «жи-ши», а потом правило про «не с глаголами», но применять мы их будем в том порядке, какой требуется в тексте.
Получается, что смысл функционального программирования в том, чтобы описать не сами чёткие шаги к цели, а правила, по которым компилятор сам должен дойти до нужного результата.
Курсы по программированию с нуля
Приходите к нам в ИТ. У нас есть удаленная работа, высокие зарплаты и удобное обучение в «Яндекс Практикуме». Старт бесплатно.

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Функциональное программирование в Java: определение, паттерны и применение
![]()
Как функциональное программирования в Java выглядит на деле. Для начала давайте создадим вот такой интерфейс:
public finish interface MyFunction
R apply(P form);
>
Потом возьмем и объявим анонимную реализацию выше описанного интерфейса. Например так:
public static void main()
// Объявим "анонимную функцию", присвоив ее значение переменной intAndString.
MyFunction intAndString = new MyFunction()
@Override public MyString apply(Integer from)
return from.AndString();
>
>;
intAndString.apply(8000); // Происходит вызов нашей «анонимной функции», где в ответ получим строку "8000".
>
Чуть выше мы показали, как реализовывается «анонимная функция». Если рассматривать ее с позиции функционального программирования, тогда она ничем не отличается от обычной функции функциональных языков программирования. То ест ь с ней можно делать все, что обычно делают программисты с функциями. Таким образом Java «превращается» из объектно-ориентированного языка в функциональный язык программирования.
Функциональное программировани е в Java: принципы
- языки, которые специально спроектированы для реализации функциональной парадигмы, например : Haskell, Erlang, F# и др. ;
- языки, которые поддерживают возможности объектно-ориентированного и функционального программирования, например : Java, JavaScript, Python, PHP, C++ и др. ;
- языки, которые не поддерживают реализацию функционального программирования.
- Переменные и функции. Это важнейшие составляющие функциональной парадигмы. С переменными в Java все в порядке, а вот с функциями нужно повозиться и реализовывать их через «анонимные» интерфейсы и методы.
- Чистые функции. Такие функции не создают проблемных эффектов и при идентичном входящем значении всегда выдают одинаковый вывод. Благодаря «чистым» функциям снижается риск возникновения ошибок в программе.
- Неизменяемые данные и состояния. После их определения данны е или состояни я н е могут видоизменяться. Благодаря этому свойству сохраняется постоянство рабочей среды для выводящих значений функции. При соблюдении этого принципа каждая функция воспроизводит один и тот же результат и не имеет зависимости от состояния программного обеспечения. Также такой принцип исключает применение функций с общим состоянием. Это когда в одно состояние программы упира е тся несколько функций.
- Рекурсия. Это способ осуществлять перебор информации в функциональном программировании без использования цикла «if. else».
- Первоклассность функций. Этот принцип позволяет применять функци ю к ак обычное значение. Например, можно заполнить функциями массив или сохранить их в переменной.
- Высший порядок функции. Этот принцип позволяет одной функции использовать другую функцию в качестве своего аргумента или возвращать ее в качестве исходящего значения.
- Композиция функций. Этот принцип подразумевает построение структуры из функций, где результат выполнения одной функции будет передаваться в другую функцию , и так дальше по цепочке. Таким образом, при помощи вызова одной функции можно спровоцировать исполнение целой цепочки функций.
Преимущество функционального программирования в Java
- более легкая отладка за счет использования «чистых» функций и неизменных данных;
- отложенное вычисление происходит за счет того, что функциональная программа вычисляется только при надобности;
- модульность достигается за счет того, что «чистые» функции можно использовать в разных областях одного кода;
- улучшенная читабельность достигается за счет того, что поведение каждой отдельной функции предсказуемо и неизменно;
- облегченное параллельное программирование;
- и др.
Заключение
Функциональное программирование в Java не является основной парадигмой, однако тоже довольно просто реализуется. Обычно функциональное программирование имеет смысл применять тогда, когда программные решения легко выражаются при помощи функций и не имеют тесной связи с реальным миром. ООП чаще всего реализуется, когда п рограмма моделируется с использованием объектов из реальной жизни. Подробнее на разнице между ФП и ООП мы остановимся в следующих статьях.
Мы будем очень благодарны
если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.