Learning Kotlin: Invoke
More Information This is the 21st post in a multipart series. If you want to read more, see our series index
Today we tackle a weird operator, invoke, which lets an instance of a class have a default function—something I’m not sure I’ve ever seen any language do. Let’s frame this with a simple example: we have a Config class that returns configuration for something:
class Config {
fun get(): String { // do stuff
return "stuff"
}
}
fun main(args: Array<String>) {
val config = Config()
println(config.get())
}
Now, in our world, maybe get is the primary use, so we can make it so that the instance config (line 9) can be called directly to get it:
class Config {
operator fun invoke(): String {
return this.get()
}
private fun get(): String { // do stuff
return "stuff"
}
}
fun main(args: Array<String>) {
val config = Config()
println(config())
}
Note: we add a new operator (line 2), and it calls the private get—though it didn’t need to be private, I thought this would be cleaner. Now, on line 14, we can just call the instance itself.
Now, you might be thinking… nice, but so what? Saving a few keystrokes isn’t that awesome. Well, invoke can return anything—even itself—opening up something truly powerful.
class Config {
var count = 0
operator fun invoke(): Config {
count++
return this
}
}
fun main(args: Array<String>) {
val config = Config()
config()()()()()()()()()()
println("config was called ${config.count} times")
}
This will print out: config was called 10 times
That’s getting more interesting, so let’s ramp up another level and pass parameters to invoke:
class Config {
var word = ""
operator fun invoke(s: String): Config {
word += s
return this
}
}
fun main(args: Array<String>) {
val config = Config()
config("R")("o")("b")("e")("r")("t")
println(config.word)
}
While I don’t yet know where I’d use this myself, I do use invoke all the time… since it’s what makes lambdas possible in Kotlin. When we create a lambda, we get an object that’s invoked with—well—invoke.