🔶 Как начать в лямбда-выражения на Java



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



Допустим, нужно получить только четные числа из массива:



ArrayList<Integer> getItemsDividedBy2(int... numbers) {

var result = new ArrayList<Integer>();

for (Integer item : numbers)

if (item % 2 == 0)

result.add(item);

return result;

}



И ещё требуется найти числа, которые делятся на семь:



ArrayList<Integer> getItemsDividedBy7(int... numbers) {

var result = new ArrayList<Integer>();

for (Integer item : numbers)

if (item % 7 == 0)

result.add(item);

return result;

}



Код точно такой же, но в первом случае у нас в метод "зашита" двойка, во втором – семёрка.



Если потребуется метод, который будет делать выборку чисел делящихся на пять, нужно будет писать ещё один метод с 99.9% повторяющегося кода



Если нужно получить выборку чисел, делящихся на 5 и на 11?



Don’t repeat yourself (DRY) – нет, не слышали...



Как обойти проблему дублирования кода?



1️⃣ описываем интерфейс с методом, принимающим число и возвращающим логическое значение:



interface Divided {

boolean by(int value);

}



2️⃣ описываем метод, который принимает условие выборки и набор чисел:



ArrayList<Integer> getItems(Divided divided, int... numbers) {

var result = new ArrayList<Integer>();

for (Integer item : numbers)

if (divided.by(item))

result.add(item);

return result;

}



Как использовать?



1️⃣ описываем класс и делаем реализацию интерфейса:



class DividedBy2 implements Divided {

@Override

public boolean by(int value) {

return value % 2 == 0;

}

}



2️⃣ Используем экземпляр класса DividedBy2 при вызове getItems

var n = getItems(new DividedBy2(), numbers);



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



Всё меняется если прописать это при помощи лямбда-выражения – не нужно описывать класс, а сразу указать так:

var n = getItems(e -> e % 3 == 0, numbers);



Лямбда строится по принципу: (параметры) -> (код метода). Если аргумент один – скобки можно опустить.



Дальше больше: можно не описывать отдельный интерфейс, потому что такой интерфейс уже есть в Java:

public interface Predicate<T>



Т е нам нужно сразу при описании метода указать тип интерфейса и использовать его действие test:



ArrayList<Integer> getItems(Predicate<Integer> divided, int... numbers) {

  var result = new ArrayList<Integer>();

for (Integer item : numbers)

if (divided.test(item))

result.add(item);

return result;

}



а клиентский код

var n = getItems(e -> e % 3 == 0, numbers);

остаётся прежним.



В Java есть и другие функциональные интерфейсы: Function<T,R>,

UnaryOperator<T>,

BinaryOperator<T>,

Consumer<T> и т.д.



Удобно. Выгодно. Используем! Исходник на GitHub



#java #ооп