Перейти к содержимому

Что такое функциональное программирование java

  • автор:

Функциональное программирование в 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

Павел Симонов
Исполнительный директор Geekbrains

Топ-30 самых востребованных и высокооплачиваемых профессий 2023

Поможет разобраться в актуальной ситуации на рынке труда

Подборка 50+ бесплатных нейросетей для упрощения работы и увеличения заработка

Только проверенные нейросети с доступом из России и свободным использованием

ТОП-100 площадок для поиска работы от GeekBrains

Список проверенных ресурсов реальных вакансий с доходом от 210 000 ₽

Получить подборку бесплатно
Уже скачали 25504

К примеру, в объектно-ориентированном программировании разработчик должен задать объекты и описать алгоритмы их взаимодействия друг с другом, но, при этом, есть возможность для написания кода, не связанного с ними. Такой код выглядит обособленным и оказывает влияние на работу всей программы. Он направляет некоторые объекты для взаимодействия между собой, осуществляет обработку определенных результатов и т.д.

Функциональная парадигма в этом плане продвинулась глубже. При таком программировании код описывает правила работы с данными. Разработчик лишь формирует необходимые условия, а код уже непосредственно определяет их практическое применение.

Сравнивая процедурное и функциональное программирование, можно отметить совпадения только в наличии команд, которые выполняет язык. Все остальные аспекты этих парадигм отличаются.

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

Сравнение императивного и функционального программирования

Можно решить, что термин «функциональный стиль программирования» говорит о функциях.

Но это не так. Функции представлены в различных парадигмах. Но в отношении функционального программирования отличие от императивного заключается в самом подходе к написанию приложений.

Для наглядности рассмотрим ситуацию с открытием кафе. Здесь предусмотрено наличие таких типов работников, как повара и администраторы.

Поварам необходимо предоставить пошаговую инструкцию по приготовлению каждого блюда. К примеру:

  1. Наполнить кастрюлю водой.
  2. Разместить кастрюлю на плите и установить определенную температуру нагрева.
  3. Посолить воду в кастрюле с использованием нужного количества соли.
  4. При приготовлении 10 порций, нужно использовать 1 свеклу, для 20 порций – 2 две свёклы и т.д.
  5. Почистить всю отобранную свеклу.

Что такое функциональное программирование

В программировании есть два больших подхода — императивное и функциональное. Они существенно отличаются логикой работы, ещё и создают путаницу в названиях. Сейчас объясним.

�� Функциональное — это про функции?

❌ Нет. Функциональное — это не про функции. Функции есть почти в любых языках программирования: и в функциональных, и в императивных. Отличие функционального программирования от императивного — в общем подходе.

Метафора: инструкция или книга правил

Представьте, что вы открываете кафе-столовую. Сейчас у вас там два типа сотрудников: повара и администраторы.

Для поваров вы пишете чёткие пошаговые инструкции для каждого блюда. Например:

  1. Налить воды в кастрюлю
  2. Поставить кастрюлю с водой на огонь
  3. Добавить в кастрюлю с водой столько-то соли
  4. Если нужно приготовить 10 порций, взять одну свёклу. Если нужно приготовить 20 порций, взять две свёклы.
  5. Почистить всю свёклу, которую вы взяли

Повар должен следовать этим инструкциям ровно в той последовательности, в которой вы их написали. Нельзя сначала почистить свёклу, а потом взять её. Нельзя посолить кастрюлю, в которой нет воды. Порядок действий важен и определяется вами. Это пример императивного программирования. Вы повелеваете исполнителем. Можно сказать, что исполнители выполняют ваши задания.

Для администратора вы пишете не инструкцию, а как бы книгу правил:

  • У нас нельзя со своим. Если гости пришли со своим, то сделать им замечание такое-то.
  • В зале должно быть чисто. Если в зале грязно, вызвать уборщика.
  • Если образовалась очередь, открыть дополнительную кассу.

Это тоже команды, но исполнять их администратор будет не в этой последовательности, а в любой на своё усмотрение. Можно сказать, что задача этого человека — исполнять функции администратора, и мы описали правила, по которым эти функции исполнять. Это пример функционального программирования.

Что такое функциональное программирование

❌ Программисты, не бомбите

Конечно же, это упрощено для понимания. Вы сами попробуйте это нормально объяснить (можно прямо в комментах).

Императивное программирование

Примеры языков: C, С++, Go, Pascal, Java, Python, Ruby

Императивное программирование устроено так:

В языке есть команды, которые этот язык может выполнять. Эти команды можно собрать в подпрограммы, чтобы автоматизировать некоторые однотипные вычисления. В каком порядке записаны команды внутри подпрограммы, в том же порядке они и будут выполняться.

Есть переменные, которые могут хранить данные и изменяться во время работы программы. Переменная — это ячейка для данных. Мы можем создать переменную нужного нам типа, положить туда какое-то значение, а потом поменять его на другое.

Если подпрограмме на вход подать какое-то значение, то результат будет зависеть не только от исходных данных, но и от других переменных. Например, у нас есть функция, которая возвращает размер скидки при покупке в онлайн-магазине. Мы добавляем в корзину товар стоимостью 1000 ₽, а функция должна нам вернуть размер получившейся скидки. Но если скидка зависит от дня недели, то функция сначала проверит, какой сегодня день, потом посмотрит по таблице, какая сегодня скидка.

Получается, что в разные дни функция получает на вход 1000 ₽, но возвращает разные значения — так работает императивное программирование, когда всё зависит от других переменных.

Последовательность выполнения подпрограмм регулируется программистом. Он задаёт нужные условия, по которым движется программа. Вся логика полностью продумывается программистом — как он скажет, так и будет. Это значит, что разработчик может точно предсказать, в какой момент какой кусок кода выполнится — код получается предсказуемым, с понятной логикой работы.

Если у нас код, который считает скидку, должен вызываться только при финальном оформлении заказа, то он выполнится именно в этот момент. Он не посчитает скидку заранее и не пропустит момент оформления.

�� Суть императивного программирования в том, что программист описывает чёткие шаги, которые должны привести код к нужной цели.

Звучит логично, и большинство программистов привыкли именно к такому поведению кода. Но функциональное программирование работает совершенно иначе.

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

Примеры языков: Haskell, Lisp, Erlang, Clojure, F#

Смысл функционального программирования в том, что мы задаём не последовательность нужных нам команд, а описываем взаимодействие между ними и подпрограммами. Это похоже на то, как работают объекты в объектно-ориентированном программировании, только здесь это реализуется на уровне всей программы.

Например, в ООП нужно задать объекты и правила их взаимодействия между собой, но также можно и написать просто код, который не привязан к объектам. Он как бы стоит в стороне и влияет на работу программы в целом — отправляет одни объекты взаимодействовать с другими, обрабатывает какие-то результаты и так далее.

Функциональное программирование здесь идёт ещё дальше. В нём весь код — это правила работы с данными. Вы просто задаёте нужные правила, а код сам разбирается, как их применять.

Если мы сравним принципы функционального подхода с императивным, то единственное, что совпадёт, — и там, и там есть команды, которые язык может выполнять. Всё остальное — разное.

Команды можно собирать в подпрограммы, но их последовательность не имеет значения. Нет разницы, в каком порядке вы напишете подпрограммы — это же просто правила, а правила применяются тогда, когда нужно, а не когда про них сказали.

Переменных нет. Вернее, они есть, но не в том виде, к которому мы привыкли. В функциональном языке мы можем объявить переменную только один раз, и после этого значение переменной измениться не может. Это как константы — записали и всё, теперь можно только прочитать. Сами же промежуточные результаты хранятся в функциях — обратившись к нужной, вы всегда получите искомый результат.

Функции всегда возвращают одно и то же значение, если на вход поступают одни и те же данные. Если в прошлом примере мы отдавали в функцию сумму в 1000 ₽, а на выходе получали скидку в зависимости от дня недели, то в функциональном программировании если функция получит в качестве параметра 1000 ₽, то она всегда вернёт одну и ту же скидку независимо от других переменных.

Можно провести аналогию с математикой и синусами: синус 90 градусов всегда равен единице, в какой бы момент мы его ни посчитали или какие бы углы у нас ещё ни были в задаче. То же самое и здесь — всё предсказуемо и зависит только от входных параметров.

Последовательность выполнения подпрограмм определяет сам код и компилятор, а не программист. Каждая команда — это какое-то правило, поэтому нет разницы, когда мы запишем это правило, в начале или в конце кода. Главное, чтобы у нас это правило было, а компилятор сам разберётся, в какой момент его применять.

В русском языке всё работает точно так же: есть правила правописания и грамматики. Нам неважно, в каком порядке мы их изучили, главное — чтобы мы их вовремя применяли при написании текста или в устной речи. Например, мы можем сначала пройти правило «жи-ши», а потом правило про «не с глаголами», но применять мы их будем в том порядке, какой требуется в тексте.

�� Получается, что смысл функционального программирования в том, чтобы описать не сами чёткие шаги к цели, а правила, по которым компилятор сам должен дойти до нужного результата.

Курсы по программированию с нуля

Приходите к нам в ИТ. У нас есть удаленная работа, высокие зарплаты и удобное обучение в «Яндекс Практикуме». Старт бесплатно.

Курсы по программированию с нуля Курсы по программированию с нуля Курсы по программированию с нуля Курсы по программированию с нуля

Получите ИТ-профессию

В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.

Функциональное программирование в Java: определение, паттерны и применение

Lorem ipsum dolor

Как функциональное программирования в 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++ и др. ;
  • языки, которые не поддерживают реализацию функционального программирования.
  1. Переменные и функции. Это важнейшие составляющие функциональной парадигмы. С переменными в Java все в порядке, а вот с функциями нужно повозиться и реализовывать их через «анонимные» интерфейсы и методы.
  2. Чистые функции. Такие функции не создают проблемных эффектов и при идентичном входящем значении всегда выдают одинаковый вывод. Благодаря «чистым» функциям снижается риск возникновения ошибок в программе.
  3. Неизменяемые данные и состояния. После их определения данны е или состояни я н е могут видоизменяться. Благодаря этому свойству сохраняется постоянство рабочей среды для выводящих значений функции. При соблюдении этого принципа каждая функция воспроизводит один и тот же результат и не имеет зависимости от состояния программного обеспечения. Также такой принцип исключает применение функций с общим состоянием. Это когда в одно состояние программы упира е тся несколько функций.
  4. Рекурсия. Это способ осуществлять перебор информации в функциональном программировании без использования цикла «if. else».
  5. Первоклассность функций. Этот принцип позволяет применять функци ю к ак обычное значение. Например, можно заполнить функциями массив или сохранить их в переменной.
  6. Высший порядок функции. Этот принцип позволяет одной функции использовать другую функцию в качестве своего аргумента или возвращать ее в качестве исходящего значения.
  7. Композиция функций. Этот принцип подразумевает построение структуры из функций, где результат выполнения одной функции будет передаваться в другую функцию , и так дальше по цепочке. Таким образом, при помощи вызова одной функции можно спровоцировать исполнение целой цепочки функций.

Преимущество функционального программирования в Java

  • более легкая отладка за счет использования «чистых» функций и неизменных данных;
  • отложенное вычисление происходит за счет того, что функциональная программа вычисляется только при надобности;
  • модульность достигается за счет того, что «чистые» функции можно использовать в разных областях одного кода;
  • улучшенная читабельность достигается за счет того, что поведение каждой отдельной функции предсказуемо и неизменно;
  • облегченное параллельное программирование;
  • и др.

Заключение

Функциональное программирование в Java не является основной парадигмой, однако тоже довольно просто реализуется. Обычно функциональное программирование имеет смысл применять тогда, когда программные решения легко выражаются при помощи функций и не имеют тесной связи с реальным миром. ООП чаще всего реализуется, когда п рограмма моделируется с использованием объектов из реальной жизни. Подробнее на разнице между ФП и ООП мы остановимся в следующих статьях.

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *