最近遇到在循环中达到某个条件之后需要退出循环的需要,在Kotlin的foreach中使用return@forEach竟然有坑,因此特地写下该篇文章记录下来。
在Kotlin的foreach中使用return@forEach是类似continue的效果,跳转至循环的下一步,如果要跳出循环,需要在循环外面标记run loop@{},然后使用return@loop跳出循环。
进入正文
跳转
Kotlin中有三种可以达到跳转目的的方式:
- return:默认情况下,从最近的闭合函数或者匿名函数返回。
- break:默认情况下,终止最近的闭合循环。
- continue:默认情况下,前进到最近的闭合循环的下一步。
所有这些表达式都可以用作更大表达式的一部分,例如
val s = person.name ?: return
标签
Kotlin中任何表达式都可以使用标签来进行标记。标签的形式是标识符后跟符号@,例如abc@或fooBar@。要标记表达式,只需在其前面添加一个标签即可。
loop@ for (i in 1..100) {
// ...
}
break
break:默认情况下,终止最近的闭合循环。
示例:
for (i in 1..5) {
for (j in 1..5) {
if (j == 3) break
Log.d("loop", "i:${i},j:${j}")
}
}
输出结果:
i:1,j:1
i:1,j:2
i:2,j:1
i:2,j:2
i:3,j:1
i:3,j:2
i:4,j:1
i:4,j:2
i:5,j:1
i:5,j:2
可以看到,break终止了最近的j循环,如果想要直接跳出外部的i循环,就可以使用标签来限定。
示例:
loop@ for (i in 1..5) {
for (j in 1..5) {
if (j == 3) break@loop
Log.d("loop", "i:${i},j:${j}")
}
}
输出结果:
i:1,j:1
i:1,j:2
continue
continue:默认情况下,前进到最近的闭合循环的下一步。
示例:
for (i in 1..5) {
for (j in 1..5) {
if (j == 3) continue
Log.d("loop", "i:${i},j:${j}")
}
}
输出结果:
i:1,j:1
i:1,j:2
i:1,j:4
i:1,j:5
i:2,j:1
i:2,j:2
i:2,j:4
i:2,j:5
i:3,j:1
i:3,j:2
i:3,j:4
i:3,j:5
i:4,j:1
i:4,j:2
i:4,j:4
i:4,j:5
i:5,j:1
i:5,j:2
i:5,j:4
i:5,j:5
可以看到,continue在最近的j循环中,跳过了3,前进到了4,如果使用标签来限定。
示例:
loop@ for (i in 1..5) {
for (j in 1..5) {
if (j == 3) continue@loop
Log.d("loop", "i:${i},j:${j}")
}
}
输出结果:
i:1,j:1
i:1,j:2
i:2,j:1
i:2,j:2
i:3,j:1
i:3,j:2
i:4,j:1
i:4,j:2
i:5,j:1
i:5,j:2
每次执行到j=3时,continue是跳过i循环当前步的接下去操作,然后前进到i循环的下一步,可以发现i循环的continue效果等同于上面的j循环的break效果。
return
默认情况下,从最近的闭合函数或者匿名函数返回。
示例:
repeat(5) {
if (it == 2) return@repeat
Log.d("loop", "repeat:${it}")
}
输出结果:
repeat:0
repeat:1
repeat:3
repeat:4
示例:
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach
Log.d("loop", "forEach:${it}")
}
输出结果:
forEach:1
forEach:2
forEach:4
forEach:5
可以看到return@repeat没有退出当前repeat函数,return@forEach也没有退出当前forEach函数,整个就是一个continue效果。
进去看一下源码:
/**
* Executes the given function [action] specified number of [times].
*
* A zero-based index of current iteration is passed as a parameter to [action].
*
* @sample samples.misc.ControlFlow.repeat
*/
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
/**
* 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)
}
发现了什么,lambda表达式,内联函数,高阶函数。
可以在最下方参考文献 Kotlin的文档上找到非常明确的解释。
如果直接使用return,会直接结束当前的整个函数,自然是不行。
可如果是使用隐式标签,@repeat和@forEach,这样的标签和传递过来的lambda表达式函数同名,所以return@repeat和return@forEach的意思也就是结束lambda表达式函数,而这仅仅只是其中一次遍历过程,所以效果等同于continue效果。
如果想要达到break的效果,就需要在外面再套一层并进行标记。
示例:
run loop@{
repeat(5) {
if (it == 2) return@loop
Log.d("loop", "repeat:${it}")
}
}
输出结果:
repeat:0
repeat:1
示例:
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop
Log.d("loop", "forEach:${it}")
}
}
输出结果:
forEach:1
forEach:2
网友评论