I'm learning Dagger2 via their tutorial but am blocked at part 5. I've implemented everything in Kotlin (jvm) and the logic/behavior matches the tutorial in working order to this point.
However, I'm unable to create SystemOutModule
and unsure whether I'm mistaken somewhere with Dagger, or in my translation of their module class.
/**
* Tutorial's Java.
*/
@Module
abstract class SystemOutModule {
@Provides
static Outputter textOutputter() {
return System.out::println;
}
}
/**
* My Kotlin.
*/
@Module
object SystemOutModule {
@JvmSuppressWildcards
@Provides
fun textOutputter(): (String) -> Unit = { println(it) }
}
The build fails with this error:
~~ ./gradlew clean build
> Task :app:kaptKotlin FAILED
e: /Users/eric/IdeaProjects/atm/app/build/tmp/kapt3/stubs/main/com/es0329/atm/CommandRouterFactory.java:7: error: [Dagger/MissingBinding] com.es0329.atm.Outputter cannot be provided without an @Provides-annotated method.
public abstract interface CommandRouterFactory {
^
com.es0329.atm.Outputter is injected at
com.es0329.atm.HelloWorldCommand(outputter)
com.es0329.atm.HelloWorldCommand is injected at
com.es0329.atm.HelloWorldModule.helloWorldCommand(command)
com.es0329.atm.Command is injected at
com.es0329.atm.CommandRouter(command)
com.es0329.atm.CommandRouter is provided at
com.es0329.atm.CommandRouterFactory.router()
FAILURE: Build failed with an exception.
Your HelloWorldCommand
expects an Outputter
, but you are providing a (String) -> Unit
instead. There are a few ways to bridge this gap:
Instead of an Outputter
, your HelloWorldCommand
can ask for a (String) -> Unit
directly.
class HelloWorldCommand @Inject constructor(private val outputter: (String) -> Unit)
This will work, but you might want to provide a different type of (String) -> Unit
elsewhere in your app. If you do, you will have to use some sort of @Qualifier
.
Fortunately, this is the only injected lambda in the entire tutorial as far as I can tell, so it won't be a problem here.
Outputter
a typealiasYou can use a typealias to make Outputter
the same type as (String) -> Unit
. This works identically to option 1, but is more expressive. Unfortunately, it has the same issue if you ever want to have another (String) -> Unit
in your dependency graph later.
typealias Outputter = (String) -> Unit
Outputter
interfaceRather than provide a lambda, you can provide an object implementing Outputter
. This is less idiomatic for a simple function, since Kotlin can't assign lambdas directly to SAM interfaces defined in Kotlin.
@Provides
fun textOutputter() = object : Outputter {
override fun output(output: String) = println(output)
}