kotlin - 无法使用 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()
在我的代码中,当我执行以下操作时:
val cm = DaggerWeaverData.builder().build().getPumps()
我确实按预期得到了三个泵。但是,当我尝试将其注入其他类时:
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>
,但我仍然遇到类似的错误。在multibinding 上的 dagger 文档中,示例(Java 中)显示以下内容:
class Bar {
@Inject Bar(Set<String> strings) {
assert strings.contains("ABC");
assert strings.contains("DEF");
assert strings.contains("GHI");
}
}
这正是我正在做的。对于基于构造函数的注入,它在 Kotlin 中工作得很好,因为以下编译并按预期运行:
class CoffeeMaker @Inject constructor(
private val heater: Heater
) {
fun makeCoffee() =
"Making coffee with heater ${heater::class.java}"
}
所以我有点不知道如何让这种多重绑定工作。
解决方案
所以事实证明你需要做的是:
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 中定义为 asSet<out E>
翻译成 Java 为Set<? extends Pump>
. 从类型论的角度来看,Set<? extends Pump>
与 不同Set<Pump>
,因此 Dagger(可能)拒绝将其Set<Pump>
视为可注入的Set<? extends Pump>
,这是公平且正确的行为。
我们遇到的问题是,对于这些集合中的任何一个,由于默认情况下它们是不可变的,因此类型声明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>
(在这种情况下,assignabiliy 是有效的,而不是相反)。所以这样做,我们使用@JvmSuppressWildcards
注解。我希望这可以帮助其他面临类似问题的人。
推荐阅读
- python - 过滤国家以应用不同的停用词
- c# - 物体不会停止移动。使用新的输入系统
- sql-server - 在 SQL Server 和 Postgres 中将 DateTime 转换为字符串
- c# - WPF C# - 如何将对象动态添加到 ListView?
- java - Hive Generic UDF:Hive 未按预期进行转换,原因是:java.lang.ClassCastException:java.util.ArrayList 无法转换为 java.util.Map
- symfony - Symfony 3 - 查询缺少标识符 id
- linux - 使用 ipmitool 通过串行局域网 (SOL) 使用 Linux kgdb
- python - 在 macos for python 上的 vscode 中安装 selenium 库
- opencv - Open CV capture read() 不读取特定视频帧 fps 59.9
- javascript - 我想加载一个已经使用flask发布请求的in vue,响应是它的路径