Kotlin之returns和jumps

最近遇到在循环中达到某个条件之后需要退出循环的需要,在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

参考文献:https://kotlinlang.org/docs/returns.html

猜你喜欢

转载自blog.csdn.net/yuantian_shenhai/article/details/131324420