Куда лучше инжектить зависимости в Dagger 2: в поля или в конструктор
Что проверяют
Понимаете ли вы, почему инъекция через конструктор считается нормой, и знаете ли, где без полей не обойтись. Следом обычно спрашивают, как внедрить зависимости в Activity или Fragment и что меняет Hilt.
Как отвечать
Для своих классов используйте конструктор, для классов, которые создаёт система, поля.
- конструктор:
class UserRepository @Inject constructor(private val api: UserApi). Зависимости видны в сигнатуре, их можно объявить черезval, объект не бывает недособранным, а в юнит-тесте его создают обычным вызовом с моками из MockK, без Dagger. Бонус: такому классу не нужен метод@Providesв модуле, фабрику Dagger сгенерирует сам; - поля: нужны там, где конструктор вызываете не вы:
Activity,Fragment,Service,BroadcastReceiver. Поле объявляется как@Inject lateinit var analytics: Analyticsи не может бытьprivate, потому что сгенерированныйMembersInjectorприсваивает его напрямую. Внедрение запускается вызовомcomponent.inject(this); - момент вызова: в
Activityдоsuper.onCreate(), воFragmentвonAttach(), иначе фрагменты, восстановленные системой, могут обратиться к ещё пустым полям; - Hilt: с
@AndroidEntryPointвызовinjectделается за вас, но механика та же, это инъекция в поля. ДляViewModelиспользуют@HiltViewModelи@Inject constructor.
Если зависимость дорогая или нужна не всегда, это решается в том же конструкторе через Lazy<T> или Provider<T>, а не переходом на поля.
Где спотыкаются
Переходят на поля, потому что «конструктор слишком большой». Семь зависимостей в конструкторе говорят о том, что класс делает слишком много, и лечится это разделением класса, а не прятанием зависимостей.
Считают, что инъекция в поля работает через рефлексию и поэтому медленная. Dagger генерирует код на этапе компиляции в обоих случаях, разница в дизайне, а не в скорости.
Смешивают подходы в одном классе: часть зависимостей в конструкторе, часть в полях. Dagger заполнит поля сразу после создания, но в тесте, где объект собирают вручную, поля останутся пустыми, и тест упадёт на UninitializedPropertyAccessException.