Search code examples
androiddependency-injectionkotlinfunctional-programmingdagger-2

Provide function dependency using Dagger 2


I would like to provide a function as dependency using 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 }
        }
    }

}

Unfortunately, it does not seem to work:

[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.

What am I missing here?


Solution

  • Yes, this can be done in Kotlin.

    You need to add @JvmSuppressWildcards at the injection site to ensure the signature matches. (source)

    I wrote the following to verify it:

    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
    

    Background: Examining @Kiskae's response, it seems the problem is that a parameter of function type in Kotlin becomes contravariant on its own parameter types when the code is converted to Java bytecode. If this doesn't make sense to you, don't worry. It's not necessary to understand it to use the technique I show above.