Эта статья выведет вас на следующий уровень в 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;
*/
Function increment = 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”