我实在想不明白这个问题,简直是无计可击。我有一个称为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
显然不是这样-它期望PersistedIntakeEntry
和IntakeEntry
。
按照上面的文档,如果你的声明有像interface Mapper<out DTO, out Entity>
一样指定的out
修饰符是可能的,但这在你的情况下不起作用,因为你的类型参数在in
的位置。
有趣的问题是为什么它与ViewModelFactory
一起工作。这似乎是KAPT中的一个错误,当它看到Nothing
时,它只是在生成的代码中省略了泛型类型参数。它使泛型绕过编译器检查(但在运行时使用它并不安全!),因为泛型大多是编译时的东西(参见java中的类型擦除)。