【问题标题】:Provide function dependency using Dagger 2使用 Dagger 2 提供函数依赖
【发布时间】:2018-07-18 15:47:38
【问题描述】:

我想使用 Dagger 2 提供一个函数作为依赖项:

@Module
class DatabaseModule {

    @Provides
    @Singleton
    fun provideDatabase(application: Application, betaFilter: (BetaFilterable) -> Boolean): Database {
        return Database(application, BuildConfig.VERSION_CODE, betaFilter)
    }

    @Provides
    @Suppress("ConstantConditionIf")
    fun provideBetaFiler(): (BetaFilterable) -> Boolean {
        return if (BuildConfig.FLAVOR_audience == "regular") {
            { it.betaOnly.not() }
        } else {
            { true }
        }
    }

}

不幸的是,它似乎不起作用:

[dagger.android.AndroidInjector.inject(T)] kotlin.jvm.functions.Function1<? 
super com.app.data.BetaFilterable,java.lang.Boolean> 
cannot be provided without an @Provides-annotated method.

我在这里错过了什么?

【问题讨论】:

    标签: android dependency-injection kotlin functional-programming dagger-2


    【解决方案1】:

    是的,这可以在 Kotlin 中完成。

    您需要在注入点添加@JvmSuppressWildcards 以确保 签名匹配。 (source)

    我写了以下来验证它:

    import dagger.Component
    import dagger.Module
    import dagger.Provides
    import javax.inject.Singleton
    
    class G constructor(val function: Function1<Int, Boolean>)
    
    @Singleton
    @Module
    class ModuleB {
        @Provides
        fun intToBoolean(): (Int) -> Boolean {
            return { it == 2 }
        }
        @JvmSuppressWildcards
        @Provides fun g(intToBoolean: (Int) -> Boolean): G {
            return G(intToBoolean)
        }
    }
    
    @Singleton
    @Component(modules = [ModuleB::class])
    interface ComponentB {
        fun g(): G
    }
    
    val componentB = DaggerComponentB.create()
    val g = componentB.g()
    println(g.function(2)) // true
    println(g.function(3)) // false
    

    背景:检查@Kiskae 的响应,似乎问题在于,当代码转换为Java 字节码时,Kotlin 中函数类型的参数在其自身的参数类型上变得逆变。如果这对您没有意义,请不要担心。使用我上面展示的技术不需要理解它。

    【讨论】:

    • 为什么没有选择这个答案?这个对我有用。我们不需要 Java。
    • 对我来说,它仅在注入位置的函数类型签名本身中注释类型时才有效。像这样:constructor(val function: (@JvmSuppressWildcards Int) -&gt; @JvmSuppressWildcards Boolean)
    【解决方案2】:

    它不起作用,因为为了允许调用具有超类型的函数来代替 lambda(可以使用 (Any) -&gt; Boolean 以及 (BetaFilterable) -&gt; Boolean),函数作为参数生成字节码以允许这样做。

    以下代码:

    object Thing
    
    fun provide(): (Thing) -> Boolean {
        TODO()
    }
    
    fun requires(func: (Thing) -> Boolean) {
        TODO()
    }
    

    导致以下签名:

    签名()Lkotlin/jvm/functions/Function1&lt;LThing;Ljava/lang/Boolean;&gt;;

    声明:kotlin.jvm.functions.Function1&lt;Thing, java.lang.Boolean&gt; provide()

    签名(Lkotlin/jvm/functions/Function1&lt;-LThing;Ljava/lang/Boolean;&gt;;)V

    声明:void requires(kotlin.jvm.functions.Function1&lt;? super Thing, java.lang.Boolean&gt;)

    -LThing (? super Thing) 和 LThing (Thing) 之间的细微差别使得这些类型与 Dagger 不兼容。

    我认为这不可能实现,您需要定义一个单独的接口,该接口不具有与Function1 相同的? super/? extends 属性。

    【讨论】:

    • 我不完全理解你的第一个解释:你是不是在某个地方忘记了一个词?
    • 简单地说,Kotlin 生成的类型略有不同,具体取决于函数何时用作返回类型或参数。由于这些类型不同,匕首无法匹配它们进行注入,从而产生错误。
    【解决方案3】:

    正如 Kiskae 的回答中所指出的,使用 kotlin 无法完成这项工作。不过,您可以使用 java 使其工作:

    @Module
    public class DatabaseModuleJava {
    
        @Provides
        @Singleton
        public Database provideDatabase(Application application, Function1<BetaFilterable, Boolean> betaFilter) {
            return new Database(application, BuildConfig.VERSION_CODE, betaFilter);
        }
    
        @Provides
        @Singleton
        @SuppressWarnings("ConstantConditions")
        public Function1<BetaFilterable, Boolean> provideBetaFiler() {
            if (BuildConfig.FLAVOR_audience.equals("regular")) {
                return betaFilterable -> !betaFilterable.getBetaOnly();
            } else {
                return betaFilterable -> true;
            }
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多