注入由抽象类型参数化的类型化类

时间:2018-01-09 22:10:53

标签: generics kotlin dagger-2

我有一个简单的界面和类:

interface Foo {
    fun value(): Int
}

class FooImpl : Foo {
    override fun value() = 100
}

现在我想为Foo创建一个工厂并能够注入它。我尝试使用以下代码:

interface FooFactory : () -> Foo

@Module
class AppModule {
    // provides FooFactory
    @Provides
    fun provideFooFactory() = object : FooFactory {
        override fun invoke() = FooImpl()
    }

    // uses FooFactory
    @Provides
    fun provideFoo(factory: FooFactory) = factory()
}

@Component(modules = [AppModule::class])
interface AppComponent {
    fun foo(): Foo
}

注入Foo的地方:

@Test
fun test() {
    val component = DaggerAppComponent.builder().build()
    val foo = component.foo()
    Assert.assertEquals(100, foo.value())
}

完美的作品!但是,我认为,将FooFactory定义为接口是一种丑陋,所以我试图替换:

interface FooFactory : () -> Foo

使用:

typealias FooFactory = () -> Foo

现在我收到编译时错误:

Error:Gradle:
  kotlin.jvm.functions.Function0<? extends net.chmielowski.daggerkotlin.Foo> 
  cannot be provided without an @Provides-annotated method.

如果我理解正确,问题是在构建过程中(在Dagger代码生成之前)内联typealias并且Dagger在找出哪个提供程序为参数化(通用)类型提供实例时遇到问题{ {1}}。

顺便说一下:如果我删除了Function0<? extends Foo>并且只在任何地方使用Foo,则问题不会发生。这意味着问题不在于泛型本身,而在于抽象类型参数化的类。

此问题的解决方案是什么?

要明确 - 使用FooImpl代替typealias背后的理由是能够写下:

interface

而不是:

@Provides
fun provideFooFactory() = { FooImpl() }

2 个答案:

答案 0 :(得分:0)

实施

@Provides fun provideFooFactory() = { FooImpl() }

表示返回类型为() -> FooImpl,会转换为Function0<? extends FooImpl>,匕首无法与Function0<? extends Foo>匹配。相反,您必须确保provideFooFactory具有正确的返回类型。

这可以通过下载来实现

@Provides fun provideFooFactory() = { FooImpl() as Foo }

或明确声明类型

@Provides fun provideFooFactory(): FooFactory = { FooImpl() }

答案 1 :(得分:0)

今天我遇到了类似的问题,当时我正在使用kotlin lambda通过匕首返回值。

DaggerModule.kt

@Provides
fun provideFoo() = {
  foo.get()
}

BarClass.kt

BarClass( private val fooFunction: () -> Foo )

我遇到的错误与您看到的匕首找不到提供的方法相同。

(如上所述)

 kotlin.jvm.functions.Function0<? extends net.chmielowski.daggerkotlin.Foo> 
 cannot be provided without an @Provides-annotated method.

对我来说,在提供程序中明确声明类型是不够的。 相反,该解决方案与kotlin和java如何处理泛型有关。基本上,当将该函数转换为Java时(匕首仍然在后台运行),它添加了 <?将Foo> 扩展到方法签名。您可以使用接收类的构造函数中的 @JvmSuppressWildcards 注释抑制此行为。

BarClass( private val fooFunction: () -> @JvmSuppressWildcards Foo )