Где пригодится интерфейс Supplier



Функциональные интерфейсы появились в java 8 и помогают писать код в функциональном стиле:



🔸 Function преобразует элемент в новое значение:

stream().map(x → x.toString())



🔸 Через Predicate передаётся условие фильтрации:

stream.filter(x → x > 5) 



🔸 Consumer используется в Stream API и библиотечных классах как терминальная операция:

list.forEach(e → System.out::println)



🔸 Supplier ничего не принимает, но возвращает значение:

Stream.generate(() -> LocalTime.now())



Function, Predicate и Concumer активно используются за пределами Stream API, а вот что делать с Supplier — не всем понятно. Зачем определять метод

void m(Supplier<List> list)

вместо void m(List list) ?



Популярные версии:

1️⃣ Кастомизация экземпляра. Передаём другой Supplier — возвращается другой экземпляр.



Для этой задачи проще указать интерфейс в параметрах



2️⃣ Реализация фабрики



Вариант из Effective Java, item 5. Источник авторитетный, но исходная цель Supplier здесь ускользает.



Во-первых, зачем передавать в параметры метода фабрику? Почему бы не создать экземпляр ранее и передать его?



Во-вторых, цель фабричного метода — упростить создание сложных объектов или отдавать разные объекты в зависимости от параметров. Supplier не содержит параметров и слабо подходит под эту задачу.



Цель Supplier — ленивая инициализация. Объект создаётся, когда он нужен, либо не создаётся вообще.



Игрушечный пример:



public Connection init(Supplier<Connection> connSupplier) {

// взять коннекшн из пула

return connSupplier.get();

}



Если выполнение не дойдёт до connSupplier, новый объект создан не будет. В варианте



public Connection init(Connection conn)



для вызова метода нужно передать УЖЕ готовый экземпляр.



Другой пример — метод Optional orElseGet(Supplier supplier). Новый объект создаётся только, если Optional пуст.



Supplier участвует в сдвиге java в сторону функциональности. Function, Predicate и Concumer организуют функции высшего порядка, а Supplier — ленивые вычисления. Полезно в двух случаях:



Когда объект может не пригодиться внутри метода

Инициализировать объект нужно в момент вызова, не раньше. Например, у объекта в конструкторе есть LocalTime.now()