查看原文
其他

Kotlin中return@forEach了个寂寞

徐宜生 鸿洋 2023-09-13
本文作者:徐宜生,原文发布于:群英传

今天在Review(copy)同事代码的时候,发现了一个问题,想到很久之前,自己也遇到过这个问题,那么就来看下吧。首先,我们抽取最小复现代码。
(1..7).forEach {
    if (it == 3) {
        return@forEach
    }
    Log.d("xys""Num: $it")
}


很简单的代码,我相信很多人都这样写过,实际上就是遍历的过程中,满足条件后就退出遍历,那么上面的代码,能实现这样的需求吗?我们来看下执行结果。
Num: 1
Num: 2
Num: 4
Num: 5
Num: 6
Num: 7


很遗憾,即使等于3之后就return了,但是然并卵,遍历依然继续执行了。相信很多写Kotlin的开发者都遇到过这个问题,其原因,还是在于语法的思维定势,我们在Kotlin的文档上,可以找到非常明确的解释。

https://kotlinlang.org/docs/returns.html
我们先来看下Kotlin中forEach的源码。
/**
* Performs the given [action] on each element.
*/

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


我们来提取下关键信息:
  • 内联函数
  • 高阶函数
发现了吗,由于高阶函数的存在,当你在高阶函数的闭包内「return」时,是结束的整个函数,当你使用「return@forEach」时,是结束当前的闭包,所以,如果你像这样写:
(1..7).forEach {
    if (it == 3) {
        return
    }
    Log.d("xys""Num: $it")
}


那么等于3之后,整个函数就被return了,那么如果你像文章开头这样写,那么等效于continue,因为你结束了当前的闭包,而这个闭包只是其中的一次遍历过程。那么我们要如何实现我们最初的需求呢?看到这样,答案其实已经呼之欲出了,那就是要return整个遍历的闭包。所以,官方也给出了解决方案,那就是外面套一层闭包:
run loop@{
    (1..7).forEach {
        if (it == 3) {
            return@loop
        }
        Log.d("xys""Num: $it")
    }
}


写起来确实是麻烦一点,但这却是必不可少的过程,是引入闭包所带来的一点副作用。
当然这里不仅限于run,任何闭包都是可以的。


最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!


推荐阅读

Android性能分析神兵利器:Perfetto
Android Studio更新后跑不起来?AGP 8「断代式」更新
视角拉高,系统性地梳理下Gradle


扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~


┏(^0^)┛明天见!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存