前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >让你迷惑的 Kotlin 代码(3)

让你迷惑的 Kotlin 代码(3)

作者头像
路遥TM
发布2021-08-31 15:43:06
4840
发布2021-08-31 15:43:06
举报
文章被收录于专栏:路遥的专栏

上一期提到了 Lambda,今天趁热打铁,再来撸一题。

代码语言:javascript
复制
fun <T> Iterable<T>.loop(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

fun numbers(list: List<Int>) {
    list.loop {
        if (it > 2) return
        print(it)
    }
    print("ok")
}

fun main(args: Array<String>) {
    numbers(listOf(1, 2, 3))
}

老规矩,猜答案。

分析一下题目。

loop() 函数的参数是函数类型,我们一般称这种参数或者返回值是函数的函数为 高阶函数loop() 函数会遍历 Iterable 的每个元素,并执行指定操作。

numbers() 函数会遍历集合 list,并且当 it > 2 的时候调用 return ,最后再打印 ok

所以问题的关键在于传入 loop 函数的 Lambda 中的 return 到底是从哪里返回?

如果是从 Lambda 返回到外层函数的话,会打印 12ok选 B

如果是从外层函数直接返回的话,会打印 12选 D

那么,答案是哪个 ?

...

...

...

答案是 A,无法编译。

不信的话,可以 CV 到 IDE 中,确实是无法编译的。Kotlin 不允许在 Lambda 表达式中这样直接使用 return 。为什么呢?个人猜测正是因为可能存在 究竟是返回到哪里 的语义不确定性,Kotlin 就直接禁止了。

再来看看下面这段代码,可以正常编译吗?

代码语言:javascript
复制
fun numbers(list: List<Int>) {
    list.forEach {
        if (it > 2) return
        print(it)
    }
    print("ok")
}

刚才的知识可能会告诉你,不可以编译。

但你又错了,是可以编译的。

foreach 是 Kotlin 标准库中定义的扩展函数。把它和之前我自己定义的 loop 比对一下。

代码语言:javascript
复制
fun <T> Iterable<T>.loop(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

唯一的区别是 foreach() 函数是用 inline 修饰的,它是一个内联函数。关于 inline,我写过一篇文章, 重学 Kotlin —— inline,包治百病的性能良药?

为什么使用 inline 修饰的高阶函数中的 Lambda 表达式中可以使用 return 呢?

因为这种情况下没有语义上的歧义。内联函数会直接将函数代码 “复制” 到函数调用处,foreach 版本的 numbers() 函数其实就等价于下面的代码:

代码语言:javascript
复制
fun numbers(list: List<Int>) {
    for (element in list) {
        if (element > 2) return
        print(element)
    }
    print("ok")
}

经过内联之后,return 被直接填充到外层函数体中,若执行到这里,直接退出函数,不存在任何歧义。

这么看来,我们被剥夺了直接从 Lambda 表达式中 return 的权利。其实并不然,Kotlin 又提供了另一个奇奇怪怪的语法来实现从 Lambda 中局部返回。

代码语言:javascript
复制
fun numbers(list: List<Int>) {
    list.loop {
        if (it > 2) return@loop
        print(it)
    }
    print("ok")
}

上面的代码最终会打印 12ok ,实现了仅从 Lambda 表达式中退出。@xxx 默认使用高阶函数名称,你也可以自定义:

代码语言:javascript
复制
fun numbers(list: List<Int>) {
    list.loop label@{
        if (it > 2) return@label
        print(it)
    }
    print("ok")
}

好像也并没有什么意义。

最后再来个奇奇怪怪的需求,inline 修饰的高阶函数使得 Lambda 表达式中可以直接使用 return 从外部函数中直接退出,但是如果我既想内联,又想禁止这一特性,即不允许 return ,该如何实现呢?

欢迎在评论区交流~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 路遥TM 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档