Dagger和Kotlin -将类绑定到其泛型超类型的问题



我实在想不明白这个问题,简直是无计可击。我有一个称为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<Any, Any>: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<SetCalorieGoalViewModel, Any, Any>类型。这些设置之间的区别是什么,使其中一个工作,而另一个不能?我怎么也弄不明白。

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

一般来说,问题是Mapper<PersistedIntakeEntry, IntakeEntry>Mapper<Any, Any>是不变的,这意味着一个不是另一个的子类型。基本上这个赋值val mapper: Mapper<Any, Any> = PersistedIntakeEntryMapper()不会编译,这就是dagger告诉你的。这是有道理的,因为Mapper<Any, Any>必须能够将Any映射到Any,而PersistedIntakeEntryMapper显然不是这样-它期望PersistedIntakeEntryIntakeEntry

按照上面的文档,如果你的声明有像interface Mapper<out DTO, out Entity>一样指定的out修饰符是可能的,但这在你的情况下不起作用,因为你的类型参数在in的位置。

有趣的问题是为什么它与ViewModelFactory一起工作。这似乎是KAPT中的一个错误,当它看到Nothing时,它只是在生成的代码中省略了泛型类型参数。它使泛型绕过编译器检查(但在运行时使用它并不安全!),因为泛型大多是编译时的东西(参见java中的类型擦除)。

最新更新