无法注入与Kotlin和Dagger的多重绑定 [英] Not able to inject a multi-binding with kotlin and dagger

查看:117
本文介绍了无法注入与Kotlin和Dagger的多重绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下定义:

@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()

在我的代码中,当我执行以下操作时:

In my code, when I do the following:

val cm = DaggerWeaverData.builder().build().getPumps()

我确实得到了预期的三个泵.但是,当我尝试将其注入其他类时:

I do get the three pumps as expected. However, when I'm trying to inject it into some other class:

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()

我也尝试注入Collection<Pump>,但是仍然出现类似的错误.在关于多重绑定的匕首文档中,该示例(在Java中)显示以下内容:

I've tried injecting Collection<Pump> also, but I still get a similar error. In the dagger docs on multibinding, the example (in Java) shows the following:

class Bar {
  @Inject Bar(Set<String> strings) {
    assert strings.contains("ABC");
    assert strings.contains("DEF");
    assert strings.contains("GHI");
  }
}

这正是我在做什么.对于基于构造函数的注入,它在Kotlin中运行良好,因为以下代码可以按预期编译并运行:

which is exactly what I'm doing. And for constructor-based injection, it is working just fine in Kotlin, because the following compiles and runs as expected:

class CoffeeMaker @Inject constructor(
    private val heater: Heater
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java}"
}

所以我对如何使这种多重绑定起作用感到困惑.

So I'm kind of at a loss on how do I get this multibinding to work.

推荐答案

因此,您需要做的是:

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>的可注射物,这是公平且正确的行为.

This is because Set is defined in Kotlin as Set<out E> which translates into Java as Set<? extends Pump>. From a type-theory perspective, Set<? extends Pump> is different from Set<Pump> and hence Dagger (probably) refuses to see Set<Pump> as an injectable for Set<? extends Pump>, which is fair and the right behavior.

我们遇到的问题是,对于这些集合中的任何一个,由于默认情况下它们是不可变的,因此类型为Set<X>的声明将转换为Set<? extends X>,因为不可变的集合仅在返回和返回时引用已解析的类型.因此是协变的.为了验证该理论,以下内容也适用:

The problem we have is that for any of these collections, since they are immutable by default, a declaration of type Set<X> will translate to Set<? extends X>, as an immutable collection only has references to the resolved type on returns and is hence covariant. To verify this theory, the following also works:

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批注.我希望这可以帮助其他面临类似问题的人.

Note the use of MutableSet, which is defined as MutableSet<E> : Set<E> .... This is probably not something one should use because I doubt that this set is actually mutable. So what we do need is for the kotlin compiler to treat Set<out E> as Set<E> (the assignabiliy is valid in this case, just not the other way around). So do so, we use the @JvmSuppressWildcards annotation. I hope this helps somebody else facing similar issues.

这篇关于无法注入与Kotlin和Dagger的多重绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆