В чем разница между Dagger Lazy и Dagger Provider

Ответ

Lazy<T> и Provider<T> в Dagger используются для отложенного получения зависимостей, но с разной семантикой:

  1. Provider
    • Каждый вызов get() создаёт новый инстанс (если зависимость не @Singleton)
    • Подходит для случаев, когда нужны множественные экземпляры
@Inject lateinit var provider: Provider<HeavyService>
// Каждый вызов создаёт новый объект:
val service1 = provider.get() 
val service2 = provider.get() // service1 != service2
  1. Lazy
    • Ленивая инициализация: объект создаётся только при первом вызове get()
    • Последующие вызовы возвращают тот же инстанс (даже для non-Singleton)
    • Потокобезопасен
@Inject lateinit var lazyService: Lazy<HeavyService>
// Объект создаётся только здесь:
val service = lazyService.get() 
// Возвращает тот же инстанс:
val sameService = lazyService.get() 

Ключевое отличие: Provider всегда создаёт новый объект, Lazy кэширует результат после первого вызова.

Ответ 18+ 🔞

А, ну это классика, ебать мои старые костыли! Сидишь такой, ковыряешься в Dagger, и тут бац — два варианта, которые вроде бы одно и то же делают, но нихуя. Lazy<T> и Provider<T>. Чувак, тут разница тоньше, чем между «пойду поем» и «пойду пожру».

Смотри, вот Provider<T> — это как джинн из бутылки, только ебаный. Каждый раз, когда ты его вызываешь через provider.get(), он выдает тебе новый объект. Представь, что у тебя есть фабрика по производству табуреток. Ты кричишь: «Эй, Provider, дай табуретку!» — и он тебе одну выкатывает. Крикнул ещё раз — он, не моргнув глазом, выкатывает вторую, абсолютно новую. Это овердохуища удобно, когда тебе нужно несколько независимых экземпляров одной и той же хрени. Например, ты создаёшь кучу одноразовых уведомлений или временных сервисов. Каждый вызов get() — это новый чел, новая жизнь.

@Inject lateinit var stoolProvider: Provider<Stool>
// Заказываем две табуретки
val stoolForMe = stoolProvider.get() // Собрали первую
val stoolForYou = stoolProvider.get() // Собрали вторую, другую!
// stoolForMe != stoolForYou — это факт, ёпта.

А теперь Lazy<T> — это уже хитрая жопа. Это не фабрика, а скорее такой ленивый кот, который спит до последнего. Ты объявляешь: «Вот тут у меня будет тяжёлый сервис, Lazy<HeavyService>». И всё. Он нихуя не создаётся. Сидит, бздит, места в памяти почти не занимает. А в тот самый момент, когда ты впервые говоришь lazyService.get(), он просыпается, охуевает от того, что его потревожили, один раз создаёт объект и навсегда его запоминает. Все последующие вызовы get() будут возвращать тебе один и тот же, уже готовый инстанс. Даже если твой сервис не помечен как @Singleton! Потокобезопасно, удобно, волнение ебать — ноль.

@Inject lateinit var lazyHeavyService: Lazy<DatabaseConnection>
// Тишина, спокойствие. Объекта ещё нет.
// ...
// Пользователь тыкнул кнопку, пора работать:
val connection = lazyHeavyService.get() // Вжух! Создалось соединение (в первый и последний раз)
// ...
// Где-то в другом месте:
val sameConnection = lazyHeavyService.get() // А вот тут уже просто вернули то, что создали ранее. Никакой магии.

Итог, чтобы впендюрить в башку:

  • Provider — «дай мне ещё одну новенькую». Каждый get() = новый объект.
  • Lazy — «создай один раз, когда попросят в первый раз, и потом давай его же». Первый get() = создание, остальные = возврат кэша.

Выбирай, исходя из того, что тебе надо: плодить сущности или один раз проинициализировать и забыть. Всё просто, как ёперный театр.