Что такое функциональный интерфейс Supplier в Java?

Ответ

Supplier<T> — это функциональный интерфейс из пакета java.util.function, который не принимает аргументов, но возвращает результат типа T. Его единственный абстрактный метод — T get().

Основное назначение: отложенная (ленивая) генерация или поставка значений.

Примеры использования:

  1. Ленивая инициализация или генерация данных:

    Supplier<String> uniqueIdSupplier = () -> UUID.randomUUID().toString();
    System.out.println(uniqueIdSupplier.get()); // Вызовет генерацию UUID
  2. Использование в Stream API и коллекторах:

    // Поставщик новой коллекции для Collectors.toCollection
    Supplier<ArrayList<String>> listSupplier = ArrayList::new;
    List<String> filteredList = someStream.collect(Collectors.toCollection(listSupplier));
  3. Реализация паттерна «Стратегия» или кэширования:

    Map<String, Supplier<Object>> strategyMap = new HashMap<>();
    strategyMap.put("A", () -> expensiveOperationA());
    strategyMap.put("B", () -> expensiveOperationB());
    
    // Операция выполнится только при вызове get()
    Object result = strategyMap.get(key).get();

Ключевые особенности:

  • Не имеет входных параметров.
  • Может выбрасывать проверяемые исключения (требует обработки внутри лямбды или обёртки).
  • Часто используется вместе с Optional.orElseGet(Supplier) для ленивого предоставления значения по умолчанию.
  • Является противоположностью Consumer<T>, который принимает значение, но ничего не возвращает.

Ответ 18+ 🔞

Давай разжую про этого Supplier<T>, а то смотрю, у некоторых глаза уже стекленеют. Представь себе такую хуйню: это как волшебный мешок, из которого можно достать что угодно, но только когда сам засунешь туда руку и скажешь «дай!». Сам по себе мешок пустой, он просто обещает, что внутри что-то есть.

По-пацански: это интерфейс с одним-единственным методом — T get(). Ничего не принимает, просто возвращает тебе что-то типа T. Всё. Больше от него нихуя.

Зачем это, спрашиваешь? А вот представь: ты не хочешь что-то вычислять или создавать прямо сейчас, потому что это может быть долго, или дорого, или вообще не факт, что пригодится. Ты говоришь: «Вот тебе, программа, инструкция, как это сделать. А когда понадобится — сам сделаешь и принесешь». Это и есть ленивая, блядь, инициализация в чистом виде.

Примеры, чтобы не быть пустобрёхом:

  1. Ленивая генерация, ёпта. Не хочешь создавать ID, пока не нужно? Вот тебе поставщик:

    Supplier<String> idGenerator = () -> UUID.randomUUID().toString();
    // Тишина. Ничего не происходит.
    // Пока не позовёшь:
    String id = idGenerator.get(); // Вот тут-то он и вспотеет, сука, и сгенерит.
  2. В Stream API — поставщик новых коллекций. Когда собираешь стрим в конкретную коллекцию, надо сказать, как именно её создавать:

    Supplier<TreeSet<String>> treeSetMaker = TreeSet::new;
    List<String> sortedUnique = someList.stream()
                                        .collect(Collectors.toCollection(treeSetMaker));
    // Коллектор будет тыкать в этого поставщика каждый раз, когда надо новый TreeSet.
  3. Паттерн «Стратегия» или ленивый кэш. Допустим, у тебя есть тяжёлые операции, и ты не хочешь их все запускать сразу, как дурак:

    Map<String, Supplier<BigDecimal>> priceCalculators = new HashMap<>();
    priceCalculators.put("VIP", () -> calculateVipPrice()); // calculateVipPrice() НЕ вызывается тут!
    priceCalculators.put("DISCOUNT", () -> calculateDiscountPrice());
    
    // А вот тут, когда ключ найден, — вызывается get() и, соответственно, тяжёлый метод.
    BigDecimal myPrice = priceCalculators.get("VIP").get();

Важные фишки, чтобы не обосраться:

  • Аргументы — ноль. Вообще. Ничего не передаёшь.
  • Исключения. Если твоя лямбда внутри может выбросить проверяемое исключение (типа IOException), тебе придётся либо завернуть его в try-catch прямо внутри, либо использовать хитрые обёртки — сам Supplier на это не рассчитан.
  • Оппозит Consumerу. Supplier — даёт, Consumer — принимает. Как я и мой холодильник.
  • Лучший друг Optional. Вместо Optional.orElse(value), который вычисляет значение всегда, даже если оно не нужно, есть Optional.orElseGet(Supplier). Поставщик сработает только если опционал пустой. Экономия, блядь, ресурсов!

Короче, Supplier<T> — это такой заготовленный пинок под жопу для твоего кода, который говорит: «Не сейчас, позже, когда скажут». Используй, когда хочешь отложить создание объекта или выполнение операции до последнего, ебать, момента.