Java был самым популярным языком с момента его запуска, потому что его создатели позаботились о том, чтобы язык ничего не упускал и сохранял свою оригинальность с учетом возникающих изменений.
Функциональное программирование
Функциональное программирование – это концепция программирования, основанная на лямбда-исчислении. В этой концепции каждое вычисление рассматривается как функция. Хотя этим функциям не должно быть разрешено изменять состояние/данные за пределами их области действия.
Почему?
Разработка программного обеспечения – это итеративный процесс, который включает в себя не только написание кода, но и понимание кода, написанного другими.
Вы можете найти это сложным, и наоборот: D
Может потребоваться много времени, чтобы просто выяснить состояние конкретного объекта, если оно было изменено функциями, которые используют его в качестве аргумента. Следовательно, это затрудняет прогнозирование поведения программы.
Рассматривайте это как сторонний API без документации. Вы не уверены в том, какого поведения следует ожидать при вызове определенной функции.
Это не то же самое с концепцией функционального программирования. Не допуская изменения состояния и изменяемых данных, он избегает побочных эффектов .
Как?
Вся эта теория прекрасна, но как я могу использовать ее как разработчик Java?
Большинство из вас, должно быть, задумывается над этим вопросом. Ответ – новый член семейства java Лямбда .
Лямбда
Лямбда – это функция, которая действует как объект. Может быть передан в любом месте. Может быть выполнен в любое время, когда это необходимо. Может быть определен в одной строке. Может быть создан без класса. И многое другое. Это помогает удалить много шаблонного кода, ранее требуемого Java.
Syntax - (Parameter Declaration) -> {Lambda Body}
Examples - Without parameters - () -> System.out.println("Hello lambda") With one parameter - s -> System.out.println(s) With two parameters - (x, y) -> x + y With multiple line body - (x, y) -> {}
Круто, правда? но как Java узнает, какая лямбда сопоставляется с какой функцией?
Копать Глубоко
Поскольку интерфейсы являются основой лямбд. Java представила новую концепцию, известную как Функциональный интерфейс , в соответствии с которой определяется лямбда.
Функциональный интерфейс
Они похожи на обычные интерфейсы в java с одним существенным отличием. Они следуют ОДНОМУ и ТОМУ ЖЕ правилу . Согласно правилу SAM, в интерфейсе допускается только один абстрактный метод (SAM).
Эта проверка может быть применена во время компиляции с помощью @Функциональный интерфейс аннотация.
Для каждой лямбды нам нужно создать новый функциональный интерфейс? Это неудобно. Верно?
Похоже, Java уже позаботилась и об этой проблеме. Пакет java.util.function содержит почти 50 функциональных интерфейсов. Крайне маловероятно, что вам может понадобиться что-то, выходящее за рамки их компетенции.
Семейство функциональных интерфейсов
Освоение всех интерфейсов может показаться многовато. Скорее, если мы просто поймем их семьи, мы сможем легко указать на правильный интерфейс.
Существует 4 семейства функциональных интерфейсов –
Потребитель – Потреблять и выбрасывать
@FunctionalInterface public interface Consumer{ void accept(T t); default Consumer andThen(Consumer super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
Он принимает объект, выполняет некоторое действие и не возвращает никаких выходных данных. Так подло: D
Пример –
ConsumerstringConsumer = string -> System.out.println(string);
Поскольку лямбда-функция и println() принимают одни и те же аргументы, это также можно записать с помощью Ссылки на метод , например –
ConsumerstringConsumer = System.out::println;
Функция – Отображение, Преобразование или Компьютер
@FunctionalInterface public interface Function{ R apply(T t); default Function compose(Function super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default Function andThen(Function super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static Function identity() { return t -> t; } }
Он принимает один объект, выполняет над ним какое-то действие и возвращает другой объект.
Пример –
FunctionstringStringFunction = String::toUpperCase;
Предикат – Проверка или фильтр
@FunctionalInterface public interface Predicate{ boolean test(T t); default Predicate and(Predicate super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate negate() { return (t) -> !test(t); } default Predicate or(Predicate super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static Predicate isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
Он принимает один объект и возвращает логическое значение. Обычно определяет правила.
Пример –
PredicatestringPredicate = String::isEmpty;
Поставщик – Создать
@FunctionalInterface public interface Supplier{ T get(); }
Он ничего не принимает, но возвращает объект. Такой щедрый: D
Пример –
SupplierstringSupplier = () -> "Hello, World";
Реальный мир
Теперь пришло время применить наши знания к какой-нибудь реальной задаче. Давайте возьмем базовую задачу программирования от HackerRank – Футляр для верблюда .
Problem - Count number of words in a camelCase string. Example - saveChangesInTheEditor Result - 5
Таким образом, наш мотив состоит не только в том, чтобы решить эту проблему, но и в том, чтобы решить ее с помощью функционального способа.
Решение простое, подсчитайте количество прописных букв в строке, и результат будет равен count +1.
Чтобы сделать это функциональным способом, нам нужно –
- Поток отдельных символов в строке и
- Предикат, который помогает фильтровать символы верхнего регистра.
Solution - /* Stream - s.chars() Predicate - Character::isUpperCase */ static long camelcase(String s) { return s.chars().filter(Character::isUpperCase).count() + 1; }
Ура!
Одна строка – и проблема решена.
Такой подход помогает разработчикам легко понять поведение фрагмента кода. Приведенную выше строку можно легко понять, чтобы отфильтровать символы верхнего регистра из строки и возвращать значение (количество символов + 1) без каких-либо побочных эффектов. Именно этого мы и хотели.
Хотя становление функциональным не ограничивается приведенным выше примером. Это то, что развивается с опытом, потому что речь идет о том, как мы подходим к проблеме.
Разработчикам Java, возможно, придется приложить некоторые дополнительные усилия, чтобы освоить этот подход, потому что наши умы обучены создавать больше кода, чем требуется.:D
Оригинал: “https://dev.to/ajiteshtiwari/go-functional-with-java-45ac”