最近在 portal.kotlin-academy.com/#/ 上看到很多关于 Kotlin 的有趣的题目。个人觉得很适合 Kotlin 爱好者,感兴趣的小伙伴可自行查阅。
【有趣的 Kotlin 】系列记录自己对每一题的理解。
0x04:Lambda runnables
fun run() {
val run: () -> Unit = {
println("Run run run!")
}
object : Runnable {
override fun run() = run()
}.run()
}
fun main(args: Array<String>) {
run()
}
以上代码,运行结果是什么?可选项:
- "Run run run!"
- Doesn't compile
- StackOverflowError
- None of the above
思考一下,记录下你心中的答案。
分析
重点关注
fun run() {
val run: () -> Unit = {
println("Run run run!")
}
object : Runnable {
override fun run() = run()
}.run()
}
最外层 run()
和 main()
是一个层级,皆为文件内函数,也就是静态函数。
run()
中定义了一个变量
val run: () -> Unit = {
println("Run run run!")
}
run
变量是类型为() -> Unit
的函数类型变量。
同时,还定义了一个匿名对象
object : Runnable {
override fun run() = run()
}.run()
定义一个实现 Runnable
接口的匿名对象,并调用其 run()
方法。而 Runnable
接口里的 run()
方法实现是调用的前面的 run
函数类型变量。
梳理清楚题目每一处 run
的含义,问题就会迎刃而解。
那么,从函数执行的入口 main
函数开始,执行顺序如下:
graph LR
main函数 --> 静态函数run --> 匿名对象run --> 函数类型run
所以最终函数类型变量 run
会被调用,执行
println("Run run run!")
因此,正确答案为:
选项1. Run run run!
题中的匿名对象可以用 Lambda 简写,如此一来,你便会看到满屏的 run
,更具有迷惑性。
fun run() {
val run: () -> Unit = {
println("Run run run!")
}
Runnable { run() }.run()
}
fun main(args: Array<String>) {
run()
}
延伸
调整一下题目中的代码
fun run() {
val run: () -> Unit = {
println("Run run run!")
}
// 新增一个 run 函数
fun run() = println("Hello World")
object : Runnable {
override fun run() = run()
}.run()
}
fun main(args: Array<String>) {
run()
}
此刻,程序运行控制台输出什么内容?
答案是:Hello World。
这里涉及到一个调用优先级的问题。
关于这个问题 StackOverflow 上有个提问:
stackoverflow.com/questions/5…
Github 上也有一篇文章做了详细介绍:
感兴趣的朋友可自行查阅。
若意图让程序输出 Run run run!,需要稍作调整:
fun run() {
val run: () -> Unit = {
println("Run run run!")
}
fun run() = println("Hello World")
object : Runnable {
override fun run() = (run)()
}.run()
}
fun main(args: Array<String>) {
run()
}
或者
fun run() {
val run: () -> Unit = {
println("Run run run!")
}
fun run() = println("Hello World")
object : Runnable {
override fun run() = run.invoke()
}.run()
}
fun main(args: Array<String>) {
run()
}
总结
- 匿名对象可以调用其所在作用域内的函数和变量。
- 函数类型变量的两种调用方式:
()
和invoke()
。 - SAM 转换 Lambda