今天在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的文档上,能够找到非常明确的解释。
kotlinlang.org/docs/return…

咱们先来看下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」「Flutter」「Kotlin」
我的语雀知识库——www.yuque.com/xuyisheng