Начнем с типового примера использования лямбда-функций в Java. Обычно это метод, который работает с коллекцией и выполняет над ней какую-то операцию, основываясь на лямбде, переданной в метод как аргумент. В будущем мы познакомимся с Java Stream API, где этот подход применяется буквально на каждом шагу. А сейчас напишем свою реализацию фильтрации, чтобы собрать все изученное вместе и закрепить.
Фильтрация — это одна из самых популярных операций над коллекциями. В реальных приложениях постоянно надо удалять из коллекций элементы, которые не соответствуют условиям выборки. Например, мы можем захотеть выбрать только тех пользователей, у которых нет друзей. Как бы мы выполнили эту задачу обычным способом:
var users = /* список пользователей */;
var filteredUsers = new ArrayList<User>();
for (var user : users) {
if (user.getFriends().isEmpty()) {
filteredUsers.add(user);
}
}
Какую бы мы ни взяли задачу, везде будет прослеживаться один и тот же обход коллекции с единственным отличием в том, как выполняется проверка. Очень похоже на сортировку, где сам алгоритм сортировки не меняется, а вот сравнение элементов зависит от того, какой порядок мы хотим получить. Применив такую же логику, мы можем спрятать обход списка, вынеся логику проверки в лямбда-функцию. Тогда на выходе мы получим такое решение:
var filteredUsers = CollectionUtils.filter(users, (u) -> u.getFriends().isEmpty());
Фильтрация реализована как статический метод, где первый параметр это коллекция пользователей, второй функция-предикат, задача которой вернуть true
или false
для проверяемого пользователя. Если возвращается true
, то элемент остается в коллекции, если false
, то элемент удаляется. В результате возвращается новая коллекция, а старая не меняется. Ниже реализация этого метода.
class CollectionUtils {
private static ArrayList<User> filter(ArrayList<User> users, Predicate<User> fn) {
var filteredUsers = new ArrayList<User>();
for (var user : users) {
if (fn.test(user)) { // запуск лямбда-функции
filteredUsers.add(user);
}
}
return filteredUsers;
}
}
Самое необычное в этом коде это Predicate<User>
. Здесь мы видим пример использования встроенного в Java функционального интерфейса Predicate<T>
, с помощью которого можно создавать функции-предикаты, то есть функции, которые внутри себя выполняют какую-то проверку и возвращают true
или false
. T
означает тип, который на вход принимает функция, в нашем случае это User
. В соответствии с интерфейсом Predicate<T>
, сама лямбда-функция вызывается через метод test()
, куда передается проверяемый в данный момент пользователь.
Для более глубокого понимания темы мы рекомендуем просмотреть видео лекцию, которая является дополнительным материалом к данному курсу:
Дополнительные материалы
- Функциональный интерфейс Predicate
- Книга Современный язык Java. Лямбда-выражения, потоки и функциональное программирование
- Книга Java. Полное руководство
- Лямбда выражения
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.