Эта статья выведет вас на следующий уровень в java. Вы готовы?
Вступление
Эта статья в основном представляет собой набор примеров нескольких инструментов, предоставляемых Java 8. Эти инструменты помогли сделать программирование более простым и оптимальным (даже с точки зрения аппаратных ресурсов), используя декларативный подход. Следовательно, нам не нужно рассказывать много деталей, а только то, чего мы хотим достичь с помощью нашего кода. Я должен также подчеркнуть тот факт, что этот пакет инструментов – это то, что разработчик java “должен” знать.
Карта
1) Поток:
В этом разделе мы погрузимся в мир потоков. Мы поймем их, как они работают и зачем они нам нужны в первую очередь. 2) Ленивые выражения:
Узнав больше о ленивых выражениях, а именно о лямбдах и ссылках в этом разделе, вы сможете сделать свой код таким ленивым. Также сделает использование вашего оборудования оптимальным. 3) Функциональные интерфейсы:
На самом деле это основной раздел, в котором вы сможете увидеть реальный пример реализации кода и поймете, как использовать все, чему вы научились. Еще более конкретные примеры будут приведены в последнем разделе. 4) Необязательный:
Этот краткий раздел вернет вас к плохим воспоминаниям о ваших первых шагах по изучению java и точно к страшному NullPointerException
. Но на этот раз вы сможете взять под контроль все, не беспокоясь о каких-либо непредсказуемых проблемах с нулевым значением.
Наконец, вы поработаете над реальными примерами и научитесь некоторым специфическим трюкам, которые изменят ваш стиль программирования к лучшему!
Чтобы точно понять, почему я решил следовать этому планированию, вам нужно понять взаимосвязь между этими частями. Потому что этот выбор тем не восстанавливает все, что касается java и функционального программирования. Тем не менее, это будет вашей главной поддержкой, чтобы узнать больше.
1) Java 8 Потоковый API
Поток Java 8 – это последовательность элементов из определенного источника, с которыми мы можем выполнять основные агрегатные операции. Этот источник может быть коллекцией, массивом или потоком – это называется конвейеризация . В отличие от коллекций, потоки не являются структурой данных. Они позволяют извлекать требуемое значение только тогда, когда оно нам нужно. Кроме того, они поддерживают отложенную оценку и изначально были созданы для использования с лямбдами (см. Вторую часть). Они содержатся в пакете java.util.stream. Существует очень большой набор операций для выполнения в потоке. Большинство из них возвращают поток, чтобы мы могли выполнять больше операций. Более того, потоки не хранят ни данные, ни изменения. Они просто выполняют внутреннюю итерацию один раз, а затем возвращают результат. Поэтому мы говорим, что они расходуемые и функциональные по своей природе . Еще одним важным моментом для потока является то, что они, возможно, неограниченны (неограниченны, поэтому нам не нужно указывать определенный размер потока). Таким образом, вы можете выполнять операции с первым и последним элементами потока, не беспокоясь о его размере или порядке элементов, поскольку они также сохраняют тот же порядок элементов, что и источник.
В этой статье мы просто приведем несколько важных и часто используемых примеров функций и операций. Если вы искали дополнительные операции, которые предлагает java, вы можете их найти здесь .
В двух словах:
в пакете java.util.stream | в пакете java.util |
только для обработки данных | структура данных в памяти |
разрешить операции только с требуемыми значениями (ленивый) | все значения должны быть вычислены, а затем сохранены (нетерпеливо) |
элементы потока посещаются только один раз в течение жизни потока (если вы хотите посетить их снова, используйте другой поток!) | мы можем выполнять операции по сбору столько, сколько захотим |
не нужен фиксированный размер. Тем не менее, мы можем получить первые/последние элементы | должен иметь определенный размер |
В следующем примере мы будем использовать следующий импорт:
импортируйте java.util. Список;
Если вы возьмете этот список друзей:
Listfriends = List.of("Mohamed","Abderrahmane","Ajar","Omar","Youssef");
Представьте, что мы хотим напечатать длину каждого имени. Обычно ваш код был бы примерно таким:
int[] r = new int[5]; for (int i = 0;i<5;i++) { r[i] = friends.get(i).length(); System.out.println(r[i]); }
Я думаю, что этот код вам понятен. Но другой способ сделать то же самое без необходимости объявлять какую-либо переменную заключается в следующем:
friends .stream()//create stream of friends .forEach(n->System.out.println( n.length()));//Then print successively each one of them
Пожалуйста, обратитесь к следующему разделу, чтобы понять, что это за выражения: t -> выражение
.
2) Ленивые выражения
Ленивые выражения или ленивая оценка – это стратегия кодирования, а также образ мышления. Вы найдете множество сложных объяснений того, что такое ленивая оценка. Тем не менее, все дело в том, как сделать ваш код более ленивым , и под этим я подразумеваю оптимальный . Это можно сделать, отложив выполнение функции до тех пор, пока нам действительно не понадобится ее выполнение. Java ленив, когда дело доходит до оценки логических аргументов, например, в f1()
f2() вызов
f2() никогда не выполняется, если
f1()
- Ссылка на метод
Ссылка на метод относится к методу из класса или объекта, использующему класс::Имя метода
синтаксис типа. В java 8 существуют различные типы доступных ссылок на методы. Например:
Класс::Имя статического метода | Математика::макс эквивалентно математике.макс(x,y) | Ссылка на статический метод |
Экземпляр класса::Имя метода экземпляра | System.out::println эквивалентно System.out.println(x) | Ссылка на метод экземпляра из экземпляра |
Класс::имя метода экземпляра | Строка::длина, эквивалентная длине строки() | Ссылка на метод экземпляра из типа класса |
Класс::новый | ArrayList::новый эквивалент нового ArrayList() | Ссылка на конструктор |
- Лямбда-выражение
Лямбда-выражение также называют анонимной функцией, т. е. функцией без имени и какого-либо идентификатора. Это безымянные функции, заданные как постоянные значения и записанные точно в том месте, где это необходимо, обычно в качестве параметра какой-либо другой функции. Они пишут в такой форме: (параметры) -> выражение
; что означает, что выражение
выполняется, когда оно вызывается, по значению, которое принимает переменные параметры
. (Например, это выражение (x, y) -> x + y
возвращает результат сложения x
и y
, также это выражение (x) ->System.out.println(x)
выводит значение, присвоенное аргументу x
;
В приведенном выше примере .Для каждого(n->System.out.println (n.длина()));
означает, что мы будем выполнять действие между скобками для каждого элемента нашего потока, а именно выводить их по одному.
Более того, приведенный выше код также может быть написан следующим образом с некоторыми небольшими изменениями:
friends .stream() .map(String::length)//Map each name to its length. As result we get a stream of lengths. .forEach(System.out::println);//Print each element of the stream.
3) Функциональные интерфейсы
Пришло время глубже погрузиться в одно из главных нововведений, с которым появилась java 8.
- В целом, функциональные интерфейсы – это интерфейсы, которые допускают внутри себя ровно один абстрактный метод в дополнение к любому количеству методов по умолчанию. Поскольку они не являются абстрактными, потому что у них есть реализация (я должен также подчеркнуть тот факт, что функции по умолчанию являются революционным дополнением в java 8). Функциональные интерфейсы также называются Интерфейсами единого абстрактного метода *(Интерфейсы SAM) *. В Java 8 функциональные интерфейсы также могут быть представлены с помощью лямбда-выражений, ссылок на методы и ссылок на конструкторы. Необязательно добавлять
@FUNCTIONAL Interface
аннотацию к функциональному интерфейсу, чтобы определить его. Например, у нас может быть что-то вроде этого:
@FunctionalInterface public interface MyInterface { public void job();//one abstract method default void fct1(){//default method //Method body } default void fct2(){//default method //Method body } static void fct(){//default static method //Method body } }
- Методы по умолчанию являются смежной темой. Поскольку это методы, которые могут иметь реализацию в интерфейсе! Эта реализация по умолчанию может быть либо сохранена как есть, либо переопределена. Мы можем создать метод по умолчанию, добавив ключевое слово
по умолчанию
или, и это так здорово, сделав егостатичным
без необходимости добавлятьзначение по умолчанию
ключевое слово. Я не буду вдаваться в подробности, которые вы можете найти позже (поверьте мне, эта новая функция потрясающая поэтому, пожалуйста, проверьте ссылки ), но хороший вопрос – что они также предоставляют, чтобы я поместил их в этот раздел? Ну, потому что методы по умолчанию включают функциональность lambdas в java. Взгляните на этот пример изjava.lang. Повторяемый
:
//This method allow you to perform action on collection default void forEach(Consumer super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } /* Before java 8 you have to iterate on your collection using the loop for. But now you can do something like this (I reused the first example here): */ friends .stream() .forEach(System.out::println);//to print all the friends names. //Or; friends .stream() .forEach(f -> System.out.println(f));
- Кроме того,
java.util
имеет набор предварительно реализованных функциональных интерфейсов, таких как следующие примеры:
Представляет функцию, которая принимает один аргумент и выдает результат. | Функция <Тип Ввода, Тип Возврата> |
Представляет операцию, которая принимает один входной аргумент и не возвращает результата. | Потребитель <Тип Ввода> |
Представляет поставщика результатов. | Поставщик <Тип Ввода> |
Представляет предикат (логически значимую функцию) одного аргумента. | Предикат <Тип Ввода> |
Я просто приведу исчерпывающий пример реализации функции
. Все остальное идет тем же путем. Тем не менее, если вам нужен полный список, вы можете проверить предоставленную документацию Oracle .
/* This example represente a method that increment its int argument by one. */ //It would be something like this: int increment(int i){ return i+1; } /* With functional interface "Function" it becomes: Don't forget to import: import java.util.function.Function; */ Functionincrement = i -> ++i;
Если вам нужны более простые примеры для этой темы, вы можете проверить мой github репозиторий .
4) Необязательный
Когда я был “новичком” в java, я ненавидел это из-за одного страшного исключения, которое постоянно появляется на моем экране: Знаменитое java Исключение NullPointerException
. Честно говоря, это было потому, что некоторые глупые, пропущенные, но необходимые начальные значения, так что java здесь не виновата. Позже я узнал об этом классе Необязательно
пакета java.util
. Необязательно, на самом деле это значение, которое может быть нулевым или не нулевым. У вас есть огромный набор инструментов и методов для манипулирования им. Если бы это было настоящее , если вы хотите выбросить конкретное исключение, если вы хотите получить значение, или еще у вас есть методы для этого в этом классе.
Итак, давайте сразу перейдем к некоторым примерам:
/*In all the example you can change the value 'null' and the SomeUserCostumizedOrAnyException by something else. */ /* If you want to print a value when only it is present. */ System.out.println(Optional .ofNullable(null) .orElseGet(() -> "value to show by default")); /* If you want to print a value when only it is present, or just throw an exception. */ // method reference System.out.println(Optional.ofNullable(null).orElseThrow(SomeUserCostumizedOrAnyException::new)); //Lambda System.out.println(Optional.ofNullable(null) .orElseThrow(()-> new SomeUserCostumizedOrAnyException("the default value is missed"))); /* If you want to check of the value is present and then print the String email(you have first change null by something like this "xyz@example.com") */ Optional.ofNullable(null).ifPresentOrElse(v-> System.out.println("Sending email to "+v+"."), () -> System.out.println("Cannot send emails!") ); /* A last comparison, in case of you want to just get the value. */ //normal method(imperative approach in which you declare all your variables and explicitly the logic of how to get the wanted result) String s="present value"; if(s==null){ System.out.println("s is null"); } else { System.out.println(s); } //new method with Optional (declarative approach in which you just describe what you want as result exactly) System.out.println(Optional.ofNullable("present value").orElse("s is null")); //Or just simply System.out.println(Optional.of("present value").get());
Вывод
Основной целью этой статьи было научить вас декларативному подходу программирования на java. Надеюсь, я достиг этой цели! Но прежде чем закончить, я просто хочу напомнить вам о том, что это должно послужить началом, и данная статья и примеры не должны быть последним, что вы увидите. Поэтому, пожалуйста, продолжайте практиковаться, чтобы учиться.
В этой статье вы узнали:
Потоки | Потоки делают ваш код ленивым, используя только требуемые значения в источнике данных. Для этого у вас есть мощный набор методов агрегирования, которые вы можете использовать |
Ленивые выражения | Лямбды и ссылка на метод изменяют ваш стиль кодирования, чтобы он был более декларативным, а также более ленивым |
Функциональные Интерфейсы | Эта часть служит теорией функционального программирования на java и некоторыми хорошими примерами для ее понимания. |
Необязательный | Этот класс много работает с потоками и функциональными интерфейсами, в случае нулевых значений. Это позволит вам избежать борьбы с исключением NullPointerException при каждом выполнении кода. |
Спасибо вам за чтение.
Рекомендации
кактодоинджава
Документация Oracle:
Функция
Течение
Оригинал: “https://dev.to/abdorah/java-functional-programming-57gd”