Dagger:如何提供参数化基类型的具体实现?

时间:2018-05-17 11:26:33

标签: android dependency-injection kotlin dagger-2 dagger

我是Dagger的新手,并进行了以下设置:

// data models
open class BaseEntity (open val id: Long)
data class UserEntity (override val id: Long, val name: String) : BaseEntity(id)
data class FruitEntity (override val id: Long, val status: String) : BaseEntity(id)

// interface to spec common API response operations
interface Repo<T> {
    fun getEntities(): List<T>
}

// entity specific implementation of the repo interface
class RepoImpl<T: BaseEntity> @Inject constructor(apiService: ApiService) : Repo<T> {
    override fun getEntities(): List<T> {
        val entities = apiService.getEntities(...)// get result from respective entity network service, e.g users, fruits etc
        return entities
    }
}

// DI: provide entity-specific implementations of the interface
@Singleton
@Provides
fun provideUserRepoImpl(userService: UserApiService): RepoImpl<BaseEntity> {
    return RepoImpl(userService)
}

@Singleton
@Provides
fun provideFruitRepoImpl(fruitService: FruitApiService): RepoImpl<BaseEntity> {
    return RepoImpl(fruitService)
}

当我构建项目时,会出现此错误:

error: RepoImpl<com.example.data.model.BaseEntity> is bound multiple times
...
@org.jetbrains.annotations.NotNull @Provides @Singleton com.example.data.source.remote.RepoImpl<com.example.data.model.BaseEntity> com.example.app.di.AppModule.provideUserRepoImpl()
@org.jetbrains.annotations.NotNull @Provides @Singleton com.example.data.source.remote.RepoImpl<com.example.data.model.BaseEntity> com.example.app.di.AppModule.provideFruitRepoImpl()

当我尝试提供特定于实体的实例时,如下所示:

@Singleton
@Provides
fun provideUserRepoImpl(userService: UserApiService): RepoImpl<UserEntity> {
    return RepoImpl(userService)
}

@Singleton
@Provides
fun provideFruitRepoImpl(fruitService: FruitApiService): RepoImpl<FruitEntity> {
    return RepoImpl(fruitService)
}

我得到以下内容:

error: RepoImpl<com.example.data.model.BaseEntity> cannot be provided without an @Inject constructor or from an @Provides-annotated method

我也试过分离水果的提供者方法。用户进入他们各自的模块,但后来的错误也出现了,也许我没有正确地连接FruitModule,UserModule和AppModule。我在提供接口实现方面遇到了类似的问题。

这里有什么正确的方法,或者根本不可能用Dagger注入参数化类?

更新

我已经设法通过显式提供要注入的每个对象来解决RepoImpl上的依赖性问题(后一个选项我们提供RepoImpl<UserEntity>RepoImpl<FruitEntity>)。我认为构造函数上的@Inject注释在某种程度上没有被注册, 所以不得不刷新项目。

但是,我还无法弄清楚如何提供参数化接口的实现(或子类型)。 例如,考虑RepoImpl的ApiService参数定义如下:

// base service for all types of items
interface ApiService<T: BaseAttributes> {
    fun getEntities(): T
}
// service impl for fruits
class FruitService : ApiService<FruitAttributes> {
    override fun getEntities(): FruitResponses {...}
}
// service impl for users
class UserService : ApiService<UserAttributes> {
    override fun getEntities(): UserResponses {...}
}

// AppModule.kt:
@Singleton
@Provides
fun provideUserService() : ApiService<UserAttributes> {
    return UserService()
}

@Singleton
@Provides
fun provideFruitService() : ApiService<FruitAttributes> {
    return FruitService()
}

错误:"... ApiService<BaseAttributes> cannot be provided without an @Provides-annotated method ..."

,而

        ...
fun provideUserService() : ApiService<BaseAttributes> {
    return UserService() as ApiService<BaseAttributes>
}
        ...
fun provideFruitService() : ApiService<FruitAttributes> {
    return FruitService() as ApiService<BaseAttributes>
}

错误:"... ApiService<BaseAttributes> is bound multiple times: ..."

我还能如何注入这些接口的实现?

1 个答案:

答案 0 :(得分:1)

原来匕首/ kotlin仿制药上有一个well-discussed wildcard issue。特别是,参数化类中的“不能在没有@Annex-annotated方法”的情况下提供错误,需要在注入站点使用@JvmSuppressWildcard注释抑制通配符类型(协变量或逆变量)的生成。所以我会做的:

class RepoImpl<T: BaseEntity> @Inject constructor(apiService: @kotlin.jvm.JvmSuppressWildcards ApiService) : Repo<T> { ... }

虽然我实际上最终将RepoImpl转换为抽象类并为Fruit&amp;创建具体的类。用户类型并在模块级提供它们:

class UserRepoImpl @Inject constructor(apiService: UserService) : RemoteRepoImpl(apiService) 
class FruitRepoImpl @Inject constructor(apiService: FruitService) : RemoteRepoImpl(apiService)

related SO question包含另一个示例。

最后,这个Kotlin+Dagger主题包含一些与此问题相关的好问题和提示