Kotlin局部返回
这个问题主要体现在forEach()
和repeat()
函数中
通常,我们会如下方式调用这两个函数:
(1..100).forEach { println(it) }
但是如果我想像通常循环一样,在特定点跳出forEach()
呢?
对于条件明确的,我们可以在forEach()
函数前明确范围,比如:
(1..100).drop(5).take(50).forEach { println(it) }
但是如果条件不明确,需要在循环中判断,这种方法就不行了
那break
呢?
直接使用break
肯定是不行的,因为这在一个lambda表达式内部
此时,你可能会说,既然是lambda内,我用return
不就完了吗?
但是如果你这么写:
(1..100).forEach {
println(it)
if (it > 50) {
return@forEach
}
}
那你就掉坑里了,看似这是让函数返回到forEach()
执行,实际上,这是一个局部返回
你只是返回到了forEach()
函数的block
而已,并没有离开forEach()
函数!
这么写可能更直观:
(1..100).forEach loop@{
println(it)
if (it > 50) {
return@loop
}
}
我们此时根本没有实现break
的效果,相反,实现的是continue
的效果
如果想要break
,就需要使用到非局部返回,查看kotlin中forEach()
函数的源代码可以发现
forEach()
函数的block
并未被标记为crossinline
或noinline
,所以是可以使用非局部返回的:
fun nonLocal() {
(1..100).forEach {
println(it)
if (it > 50) {
return@nonLocal
}
}
}
这样我们就实现了break
效果,成功从forEach()
函数离开了
你可能会觉得这也太不方便了,难道我用到forEach()
的地方想要break
,就要套一层函数吗?
其实不然,kotlin中,我们有可以随处运行的run()
函数
当你需要break一个forEach的时候,只要用run函数包裹就可以了:
run {
(1..100).forEach {
println(it)
if (it > 50) {
return@run
}
}
}
// other logic
其实不仅仅是repeat()
和forEach()
,所有传入lambda的函数,都是如此工作的
一般的函数中因为不存在循环,所以我们直接用局部返回,回到调用函数后,它完成其他工作自然就退出了
但是这类存在循环的函数中,如果对局部返回和非局部返回的理解不到位
就会发生 “我明明return了,为什么还是执行了多次?” 这样的问题