Кинжал: Как обеспечить конкретную реализацию параметризованного базового типа? - PullRequest
0 голосов
/ 17 мая 2018

Я новичок в 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?

UPDATE

Мне удалось решить проблему зависимости от RepoImpl, явно указав каждый объект для инъекции (последний вариант, где мы предоставляем RepoImpl<UserEntity> и RepoImpl<FruitEntity>). Я думаю, что аннотация @Inject на конструкторе как-то не регистрировалась, поэтому пришлось обновить проект.

Однако я пока не могу понять, как обеспечить реализацию (или подтипы) параметризованных интерфейсов. Рассмотрим, например, параметр ApiService в RepoImpl, определенный следующим образом:

// 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 голосов
/ 18 мая 2018

Оказывается, существует хорошо обсуждаемая проблема с подстановочными знаками на дженгерах / котлинах. В частности, ошибка «не может быть предоставлена ​​без аннотированного метода @ Provides» для параметризованных классов требует подавления генерации групповых типов (ковариантов или контравариантов) с помощью аннотации @JvmSuppressWildcard в месте внедрения. Так что я бы сделал:

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

Хотя я фактически закончил преобразование RepoImpl в абстрактный класс и создание конкретных классов для типов Fruit & User и предоставление их на уровне модуля:

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

Этот связанный вопрос SO содержит еще один пример.

Наконец, эта Kotlin + Dagger ветка содержит некоторые полезные советы и подсказки, относящиеся к этой проблеме

...