Можно ли создать Dagger модуль в котором будут два метода которые возвращают одинаковый тип данных?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Можно ли создать модуль Dagger с двумя методами, возвращающими одинаковый тип?
Да, это возможно, но с критически важными оговорками. В Dagger 2 модуль может содержать несколько методов @Provides, возвращающих одинаковый тип, но вы должны явно разрешить конфликт привязок, иначе компиляция завершится с ошибкой [Dagger/DuplicateBindings]. Dagger требует однозначности для каждой зависимости во время компиляции.
Почему это вызывает проблему?
Dagger строит граф зависимостей, где каждый ключ (тип + квалификатор) должен иметь ровно одну привязку. Два метода, возвращающих String без квалификаторов, создают два разных источника для одного ключа, что приводит к неоднозначности.
Решения конфликта
1. Использование @Named или пользовательских @Qualifier
Самый частый способ. Вы создаете квалификаторы — аннотации, которые "маркируют" зависимости, делая ключи уникальными.
import javax.inject.Named
import dagger.Module
import dagger.Provides
@Module
class AppModule {
@Provides
@Named("ApiKey")
fun provideApiKey(): String {
return "secret_api_key_123"
}
@Provides
@Named("BaseUrl")
fun provideBaseUrl(): String {
return "https://api.example.com/"
}
}
Или с собственным квалификатором:
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class ApiKey
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BaseUrl
@Module
class AppModule {
@Provides
@ApiKey
fun provideApiKey(): String { ... }
@Provides
@BaseUrl
fun provideBaseUrl(): String { ... }
}
Затем при инъекции указываете, какую именно реализацию нужно использовать:
class MyRepository @Inject constructor(
@Named("ApiKey") private val apiKey: String,
@Named("BaseUrl") private val baseUrl: String
)
2. Объединение в @Binds или предоставление через интерфейс
Если типы — реализации одного интерфейса, можно использовать @Binds в абстрактном модуле.
interface DataSource
class LocalDataSource : DataSource
class RemoteDataSource : DataSource
@Module
abstract class DataSourceModule {
@Binds
abstract fun bindLocal(local: LocalDataSource): DataSource
// Но так нельзя! Тоже будет конфликт. Нужны квалификаторы:
@Binds
@Named("Local")
abstract fun bindLocal(local: LocalDataSource): DataSource
@Binds
@Named("Remote")
abstract fun bindRemote(remote: RemoteDataSource): DataSource
}
3. Предоставление разных экземпляров в зависимости от контекста
Иногда логика создания отличается, но тип тот же.
@Module
class NetworkModule {
@Provides
@Named("OkHttp")
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().build()
}
@Provides
@Named("LoggingOkHttp")
fun provideLoggingOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor())
.build()
}
}
4. Использование @IntoSet, @IntoMap или @ElementsIntoSet
Если вам нужна коллекция всех объектов данного типа, Dagger позволяет собрать их в Set или Map.
@Module
class PluginModule {
@Provides
@IntoSet
fun providePluginA(): Plugin = PluginA()
@Provides
@IntoSet
fun providePluginB(): Plugin = PluginB()
// Теперь можно инжектить Set<Plugin>
}
Что произойдет без квалификаторов?
Компиляция упадет с ошибкой, например:
error: [Dagger/DuplicateBindings] java.lang.String is bound multiple times
Практический пример с двумя Retrofit-клиентами
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class AuthRetrofit
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class PublicRetrofit
@Module
class NetworkModule {
@Provides
@AuthRetrofit
fun provideAuthRetrofit(
@Named("AuthBaseUrl") baseUrl: String,
authInterceptor: AuthInterceptor
): Retrofit {
return Retrofit.Builder()
.baseUrl(baseUrl)
.client(OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.build())
.build()
}
@Provides
@PublicRetrofit
fun providePublicRetrofit(
@Named("PublicBaseUrl") baseUrl: String
): Retrofit {
return Retrofit.Builder()
.baseUrl(baseUrl)
.build()
}
}
class MyService @Inject constructor(
@AuthRetrofit private val authRetrofit: Retrofit,
@PublicRetrofit private val publicRetrofit: Retrofit
) {
// Используем разные клиенты для разных API
}
Итог
- Создать модуль с двумя методами одинакового возвращаемого типа — можно.
- Без квалификаторов — нельзя, Dagger выдаст ошибку компиляции.
- Основные решения:
@Named, кастомные@Qualifier,@IntoSet/Map. - Квалификаторы делают граф зависимостей явным и безопасным, что является одним из преимуществ Dagger перед другими DI-фреймворками.
Это поведение соответствует принципу явности и детерминированности Dagger: все зависимости должны быть однозначно разрешимы на этапе компиляции, что исключает runtime-ошибки из-за неоднозначности внедрения.