最近在 http://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()
}

以上代码,运行结果是什么?可选项:

  1. “Run run run!”
  2. Doesn’t compile
  3. StackOverflowError
  4. 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 的含义,问题就会迎刃而解。
Every Run

那么,从函数执行的入口 main 函数开始,执行顺序如下:

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 上有个提问:

https://stackoverflow.com/questions/51800008/how-resolving-kotlin-functions-properties-with-the-same-name-work

Github 上也有一篇文章做了详细介绍:

https://github.com/JetBrains/kotlin/blob/master/spec-docs/NameResolution.adoc

感兴趣的朋友可自行查阅。

若意图让程序输出 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()
}

总结

  1. 匿名对象可以调用其所在作用域内的函数和变量。
  2. 函数类型变量的两种调用方式:()invoke()
  3. SAM 转换 Lambda
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐