【问题标题】:Dagger and Kotlin - Issue with binding a class to its generic supertypeDagger 和 Kotlin - 将类绑定到其泛型超类型的问题
【发布时间】:2021-11-15 07:16:19
【问题描述】:

我现在正用头撞墙,因为我想不通。 我有一个名为Mapper 的通用接口,它有两个通用类型参数。现在我想利用多重绑定并将这个接口的多个实现绑定到Map<Class<out Any>, Provider<Mapper<Any, Any>> 类型的映射中。我的代码如下所示:

interface Mapper<DTO, Entity> {
    
    fun toEntity(model: DTO): Entity
    
    fun toDto(model: Entity): DTO
    
}


class PersistedIntakeEntryMapper @Inject constructor() : Mapper<PersistedIntakeEntry, IntakeEntry> {

    override fun toEntity(model: PersistedIntakeEntry): IntakeEntry { TODO() }

    override fun toDto(model: IntakeEntry): PersistedIntakeEntry { TODO() }

}

@Module
interface MapperModule {

    @Binds
    @IntoMap
    @MapperKey(PersistedIntakeEntry::class)
    @ModelMappers
    fun bindPersistedIntakeEntryMapper(mapper: PersistedIntakeEntryMapper): Mapper<Any, Any>

}

@Singleton
class MapperFactory @Inject constructor(
    @ModelMappers val mappers: Map<Class<out Any>, @JvmSuppressWildcards Provider<Mapper<Any, Any>>>,
) {

    @Suppress("UNCHECKED_CAST")
    inline fun <reified DTO: Any, Entity> get(): Mapper<DTO, Entity>? {
        TODO()
    }

}

Dagger 特别抱怨PersistedIntakeEntryMapper 不能分配给Mapper&lt;Any, Any&gt;MapperModule.java:13: error: @Binds methods' parameter type must be assignable to the return type
然而:奇怪的是,我对另一个像魅力一样工作的组件有相同的设置:

interface ViewModelFactory<VM : ViewModel, SavedState, Parameters> {

   fun create(savedState: SavedState?, parameters: Parameters?): VM

}


class SetCalorieGoalViewModelFactory @Inject constructor(
    private val getCalorieGoalUseCase: GetCalorieGoalUseCase,
    private val setCalorieGoalUseCase: SetCalorieGoalUseCase,
    private val navigator: Navigator,
) : ViewModelFactory<SetCalorieGoalViewModel, SetCalorieGoalUiState, Nothing> {

    override fun create(savedState: SetCalorieGoalUiState?, parameters: Nothing?): SetCalorieGoalViewModel {
        TODO()
    }

}


@Module
interface SetCalorieGoalUiModule {

    @Binds
    @IntoMap
    @ViewModelKey(SetCalorieGoalViewModel::class)
    fun bindSetCalorieGoalViewModelFactory(factory: SetCalorieGoalViewModelFactory)
            : ViewModelFactory<ViewModel, Any, Any>

}

我可以毫无问题地将SetCalorieGoalViewModelFactory 绑定到ViewModelFactory&lt;SetCalorieGoalViewModel, Any, Any&gt; 类型。这些设置使其中一个工作而另一个不工作的设置之间有什么区别?我这辈子都想不通。非常感谢任何试图解决此问题的人。

【问题讨论】:

  • 我认为是因为interface Mapper&lt;DTO, Entity&gt;中的类型没有定义。意思是,它也可以为空。所以制作这个interface Mapper&lt;DTO : Any, Entity : Any&gt; 看看它是否有效
  • 好声,感谢您的回复,但不幸的是它没有帮助,仍然是同样的问题。

标签: kotlin dagger-2 dagger


【解决方案1】:

首先,查看kotlin documentation 的泛型方差主题以及相关的 java 主题(因为 dagger 生成 java 代码)。

通常问题是Mapper&lt;PersistedIntakeEntry, IntakeEntry&gt;Mapper&lt;Any, Any&gt; 是不变的,这意味着一个不是另一个的子类型。基本上这个赋值 val mapper: Mapper&lt;Any, Any&gt; = PersistedIntakeEntryMapper() 不会编译,这就是 dagger 告诉你的。这是有道理的,因为Mapper&lt;Any, Any&gt; 必须能够将Any 映射到Any,而PersistedIntakeEntryMapper 显然不是这种情况——它需要PersistedIntakeEntryIntakeEntry

按照上面的文档,如果您的声明指定了out 修饰符,如interface Mapper&lt;out DTO, out Entity&gt;,则可能,但这不适用于您的情况,因为您的类型参数位于in 位置。

有趣的问题是为什么它适用于ViewModelFactory。这似乎是 KAPT 中的一个错误,当它看到Nothing 时,它只是在生成的代码中省略了泛型类型参数。它使它绕过编译器检查(但它不能在运行时安全使用!),因为泛型主要是编译时的东西(参见 type erasure in java)。

【讨论】:

  • 感谢周到的回复,让我明白了很多。这里有趣的事情绝对是它与 ViewModelFactory 一起工作的原因,因为将 SetCalorieGoalViewModelFactory 分配给类型 ViewModelFactory&lt;Any, Any, Any&gt; 也会导致编译器错误。所以绝对有理由假设这是由于 kapt 中的一个错误。由于我在MapperFactory 中进行了未经检查的强制转换,因此代码无论如何都不是运行时安全的,所以我解决这个问题的方法是引入一个非通用的BaseMapper 接口来绑定特定的映射器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多