kotlin - 无法使用 kotlin 和 dagger 注入(inject)多重绑定(bind)

标签 kotlin dagger-2

我有以下定义:

@Module
class WeaverDataModule {
    // Provide the three pumps from providers
    // All of them still explicitly mark 'Pump' as their return type
    @Provides @IntoSet fun providesPump(thermosiphon: Thermosiphon) : Pump = thermosiphon
    @Provides @IntoSet fun providesAnotherPump(suctionBased: SuctionBased) : Pump = suctionBased
    @Provides @IntoSet fun providesGenericPump(genericPump: GenericPump) : Pump = genericPump
}

@Component(modules = [WeaverDataModule::class])
interface WeaverData {
    // Get the CoffeeMaker
    fun coffeeMaker(): CoffeeMaker
    // Get the list of pumps
    fun getPumps() : Set<Pump>
}

interface Pump

// The three pumps
class Thermosiphon @Inject constructor(val heater: Heater) : Pump
class SuctionBased @Inject constructor() : Pump
class GenericPump @Inject constructor() : Pump

// Some random heater
class Heater @Inject constructor()

在我的代码中,当我执行以下操作时:
val cm = DaggerWeaverData.builder().build().getPumps()

我确实按预期得到了三个泵。但是,当我尝试将其注入(inject)其他类时:
class CoffeeMaker @Inject constructor(
    private val heater: Heater,
    private val pump: Set<Pump>
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java} and using pumps" +
                " ${pump.map { it::class.java }.joinToString(",")}"
}

我收到以下错误:
e: .../WeaverData.java:7: error: [Dagger/MissingBinding] java.util.Set<? extends weaver.Pump> cannot be provided without an @Provides-annotated method.                    
public abstract interface WeaverData {
                ^
      java.util.Set<? extends weaver.Pump> is injected at
          weaver.CoffeeMaker(…, pump)
      weaver.CoffeeMaker is provided at
          weaver.WeaverData.coffeeMaker()

我试过注入(inject) Collection<Pump>也,但我仍然遇到类似的错误。在 dagger docs on multibinding ,示例(在 Java 中)显示以下内容:
class Bar {
  @Inject Bar(Set<String> strings) {
    assert strings.contains("ABC");
    assert strings.contains("DEF");
    assert strings.contains("GHI");
  }
}

这正是我正在做的。对于基于构造函数的注入(inject),它在 Kotlin 中工作得很好,因为以下编译并按预期运行:
class CoffeeMaker @Inject constructor(
    private val heater: Heater
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java}"
}

所以我有点不知道如何让这种多重绑定(bind)工作。

最佳答案

所以事实证明你需要做的是:

class CoffeeMaker @Inject constructor(
    private val heater: Heater,
    private val pumps: Set<@JvmSuppressWildcards Pump>
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java} with pumps ${pumps.map { it::class.java }.joinToString(",")}"
}

这是因为 Set在 Kotlin 中定义为 Set<out E>翻译成 Java 为 Set<? extends Pump> .从类型论的角度来看,Set<? extends Pump>不同于 Set<Pump>因此Dagger(可能)拒绝看到Set<Pump>作为 Set<? extends Pump> 的注入(inject)剂,这是公平和正确的行为。

我们遇到的问题是,对于这些集合中的任何一个,由于默认情况下它们是不可变的,因此类型为 Set<X> 的声明将转换为 Set<? extends X> ,作为不可变集合仅在返回时引用解析的类型,因此是协变的。为了验证这一理论,以下方法也有效:
class CoffeeMaker @Inject constructor(
    private val heater: Heater,
    private val pumps: MutableSet<Pump>
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java} with pumps ${pumps.map { it::class.java }.joinToString(",")}"
}

注意 MutableSet 的使用, 定义为 MutableSet<E> : Set<E> ... .这可能不是一个应该使用的东西,因为我怀疑这个集合实际上是可变的。所以我们需要的是让 kotlin 编译器来处理 Set<out E>Set<E> (在这种情况下,可分配性是有效的,而不是相反)。所以这样做,我们使用 @JvmSuppressWildcards注解。我希望这可以帮助其他面临类似问题的人。

关于kotlin - 无法使用 kotlin 和 dagger 注入(inject)多重绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55412910/

相关文章:

java - 为什么我得到的是 null 而不是 @Inject-ed 字段?

arrays - Kotlin - 使用 Array<Double> 或 DoubleArray

android - Dagger 2 inject() 包含一个依赖循环

java - Dagger2 看不到在其他模块中定义的带有@Provide 注解的方法。没有 @provides-annotated 方法就无法提供

dagger-2 - 错误 : cannot find symbol @dagger. 模块(包括 = {InflationInject_ViewModule.class})

android - 如果没有 @Provides 注解的方法,则无法提供 Kotlin Dagger ViewModel

java - 改造发送 Pojo 作为表单数据

android - Android Studio 中的 Kotlin 构建错误 << intent.putExtra ("string", it.getString ("string") >>

oop - 子接口(interface)的类型参数有 "inconsistent values"

kotlin - 如何获取数组 Kotlin 中特定值的大小