1. 概述

在 Kotlin 中,函数是一等公民(first-class citizens),我们可以将函数作为局部函数或函数字面量传递,尤其是在 lambda 表达式中。这就导致了在很多场景下,会出现函数嵌套函数的情况。

在本篇简短教程中,我们会介绍在 Kotlin 中如何从嵌套结构中的某个特定函数返回,而不是默认的最近封闭函数。

2. 带标签的返回(Return at Label)

在 Kotlin 中,默认情况下,return 会从最近的封闭函数返回。例如:

fun <T> List<T>.findOne(x: T): Int {
    forEachIndexed { i, v ->
        if (v == x) {
            return i
        }
    }

    return -1
}

在这个例子中,return i 会直接返回到 findOne 函数的调用者。

但有时候我们希望只是从 lambda 表达式中返回,而不是外层函数。这个时候就可以使用带标签的返回:return@label

默认情况下,标签是接收该 lambda 的函数名,比如 forEachIndexed

fun <T> List<T>.printIndexOf(x: T) {
    forEachIndexed { i, v ->
        if (v == x) {
            print("Found $v at $i")
            return@forEachIndexed // 仅从 lambda 返回
        }
    }
}

⚠️ 这种方式称为“qualified return”,也叫“return@”。

  • return@forEachIndexed 不会终止整个 printIndexOf() 函数,只是跳出当前 lambda。
  • 这样可以让外层函数继续执行后续逻辑。

我们也可以自定义标签名,而不是使用默认的函数名:

fun <T> List<T>.printIndexOf2(x: T) {
    forEachIndexed loop@{ i, v ->
        if (v == x) {
            print("Found $v at $i")
            return@loop
        }
    }
}

✅ 自定义标签需要在 lambda 表达式前加上 label@ 的形式。

更复杂的情况,比如嵌套 lambda,也可以使用这种方式精确控制返回层级:

fun <T> List<List<T>>.nestedFind(x: T) {
    forEach { 
        it.forEachIndexed { i, v -> 
            if (v == x) {
                println("Found $v at $i")
                return@forEach
            }
        }
    }
}

⚠️ 注意:上面的 return@forEach 实际返回的是外层的 forEach,而不是内层的。

替代方案:使用匿名函数

如果不想用标签,也可以考虑使用匿名函数(anonymous function),这样普通的 return 就能直接返回该匿名函数本身:

fun <T> List<T>.printIndexOfAnonymous(x: T) {
    forEachIndexed(fun(i: Int, v: T) {
        if (v == x) {
            print("Found $v at $i")
            return
        }
    })
}

✅ 使用匿名函数时,return 只会退出当前函数体,不影响外层函数执行,是一种更直观的写法。

3. 小结

在 Kotlin 中,我们可以通过以下方式控制返回行为:

方式 说明
return 返回最近的封闭函数(如外层函数)
return@label 返回特定标签所标识的函数或 lambda
自定义标签 label@ 可以用于 lambda 表达式,增强可读性
匿名函数 使用 fun 定义的 lambda,支持普通 return

掌握这些技巧,能有效避免在嵌套函数结构中踩坑,写出更清晰、可控的代码。

完整示例代码可以在这里查看:GitHub


原始标题:Return at Label in Kotlin