1. 概述
本文深入探讨 Kotlin 中的结构化跳转表达式(structural jump expressions)。
简而言之,**Kotlin 提供了三种结构化跳转关键字:return
、break
和 continue
**。接下来我们将结合是否使用标签(label)两种情况,详细解析它们的行为和实际应用场景。对于熟悉 Java 的开发者来说,这些关键字看似眼熟,但在 Kotlin 中结合 lambda 和标签的用法更为灵活,稍不注意就容易踩坑 ❌。
2. Kotlin 中的标签(Label)
在 Kotlin 中,几乎任何表达式都可以被打上标签。
标签的定义方式是:一个标识符后跟 @
符号,例如 abc@
、loop@
都是合法的标签。
要为某个表达式添加标签,只需将其放在表达式前面即可:
loop@ for (i in 1..10) {
// some code
}
这个特性在嵌套循环或函数式编程中尤为有用,能精准控制程序流跳转的目标。
3. break 语句
基本用法(无标签)
✅ 不带标签的 break
会终止最近一层的封闭循环。
示例:
@Test
fun givenLoop_whenBreak_thenComplete() {
var value = ""
for (i in "hello_world") {
if (i == '_') break
value += i.toString()
}
assertEquals("hello", value)
}
当遇到 '_'
字符时,循环立即退出,最终 value
为 "hello"
。
带标签的 break
✅ 使用标签时,break@label
会跳出对应标签所标记的外层循环。
@Test
fun givenLoop_whenBreakWithLabel_thenComplete() {
var value = ""
outer_loop@ for (i in 'a'..'d') {
for (j in 1..3) {
value += "" + i + j
if (i == 'b' && j == 1)
break@outer_loop
}
}
assertEquals("a1a2a3b1", value)
}
在这个例子中,当 i == 'b'
且 j == 1
时,直接跳出 outer_loop@
标记的外层循环,因此后续的 'c'
和 'd'
不再执行。
⚠️ 踩坑提醒:如果不加标签,break
只能跳出内层循环,这是新手常犯错误。
4. continue 语句
基本用法(无标签)
✅ 不带标签的 continue
跳过当前迭代,进入最近一层循环的下一次迭代。
@Test
fun givenLoop_whenContinue_thenComplete() {
var result = ""
for (i in "hello_world") {
if (i == '_') continue
result += i
}
assertEquals("helloworld", result)
}
遇到 '_'
时跳过拼接操作,继续处理下一个字符。
带标签的 continue
✅ 使用标签时,continue@label
会跳转到指定标签对应的循环进行下一次迭代。
@Test
fun givenLoop_whenContinueWithLabel_thenComplete() {
var result = ""
outer_loop@ for (i in 'a'..'c') {
for (j in 1..3) {
if (i == 'b') continue@outer_loop
result += "" + i + j
}
}
assertEquals("a1a2a3c1c2c3", result)
}
一旦 i == 'b'
,整个内层循环被跳过,直接进入外层循环的下一轮(即 i='c'
),所以 'b1', 'b2', 'b3'
都不会被拼接到结果中。
📌 小技巧:这种写法在多层嵌套中比布尔标志位更清晰、更高效。
5. return 语句
默认行为(无标签)
✅ 无标签的 return
从最近的 命名函数或匿名函数返回。
@Test
fun givenLambda_whenReturn_thenComplete() {
var result = returnInLambda();
assertEquals("hello", result)
}
private fun returnInLambda(): String {
var result = ""
"hello_world".forEach {
if (it == '_') return result // 直接从 returnInLambda 函数返回
result += it.toString()
}
return result
}
⚠️ 注意:这里的 return
是从整个函数 returnInLambda()
返回,而不是仅退出 lambda。这也是为什么最后一行 return result
实际上无法到达(unreachable code)。
在匿名函数中使用 return
✅ 匿名函数支持局部 return
,可用于模拟 continue
行为:
@Test
fun givenAnonymousFunction_return_thenComplete() {
var result = ""
"hello_world".forEach(fun(element) {
if (element == '_') return // 从匿名函数返回,相当于 continue
result += element.toString()
})
assertEquals("helloworld", result)
}
因为 return
在匿名函数内部只退出该函数本身,不影响外部 forEach
的执行流程。
Lambda 中的 labeled return
Lambda 表达式本身不能直接使用无标签的 return
(会报错),但可以通过 显式标签 或 隐式标签 实现类似效果。
显式标签返回
@Test
fun givenLambda_whenReturnWithExplicitLabel_thenComplete() {
var result = ""
"hello_world".forEach lit@{
if (it == '_') {
return@lit // 返回到 lambda 外部,继续下一次 forEach
}
result += it.toString()
}
assertEquals("helloworld", result)
}
隐式标签返回(推荐写法)
@Test
fun givenLambda_whenReturnWithImplicitLabel_thenComplete() {
var result = ""
"hello_world".forEach {
if (it == '_') {
return@forEach // 使用函数名作为隐式标签
}
result += it.toString()
}
assertEquals("helloworld", result)
}
✅ 推荐使用 return@forEach
这种写法,语义清晰且无需额外定义标签。
使用 return 模拟 break 效果(跳出 lambda 外部代码块)
有时我们需要从 lambda 内部“跳出”到外层作用域,这就需要用到 带标签的代码块,比如 run {}
、with {}
等。
@Test
fun givenAnonymousFunction_returnToLabel_thenComplete() {
var result = ""
run loop@{
"hello_world".forEach {
if (it == '_') return@loop // 跳出整个 run 块
result += it.toString()
}
// 如果没有 break,这里会被执行
result += "_finished"
}
assertEquals("hello", result) // 注意:_finished 并未拼接
}
📌 场景说明:这相当于在 lambda 中实现了 break
的逻辑——提前终止外层代码块的执行。
6. 总结
本文系统梳理了 Kotlin 中 return
、break
、continue
的使用方式,重点包括:
- ✅
break
/continue
支持标签,可精准控制嵌套循环跳转 - ✅
return
在命名函数中默认返回整个函数 - ✅ 匿名函数支持局部
return
,适合实现continue
类似逻辑 - ✅ Lambda 必须使用
return@label
才能实现局部退出 - ✅ 利用
run { }
+ labeled return 可实现从 lambda “跳出”外层逻辑,类似break
这些特性在处理复杂逻辑(如状态机、嵌套遍历、事件过滤)时非常实用,但也容易误用。建议结合 IDE 提示和单元测试确保跳转逻辑符合预期。
完整示例代码可在 GitHub 获取:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-lang-2