Kotlin return@label到底怎么用

在 Kotlin 中,经常对 return@xxx 感到迷惑:

  • return@forEach 是什么意思?
  • 为什么不能直接 return
  • @label 是什么东西?是 goto 吗? 本文将结合示例 + 源码+ java代码理解 Kotlin 控制流标签用法。

开头结论

return@label 的本质是:

从当前标签所标记的 lambda 或表达式块中跳出,并继续执行其后的代码。

结合场景分析

forEach 中正确跳过当前项

java 复制代码
listOf(1, 2, 3, 4).forEach {
    if (it == 2) return@forEach
    println(it)
}

Kotlin源码:

java 复制代码
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

说明:

  • action 是一个 (T) -> Unit 类型的 lambda
  • return@forEach 实际上是指:从当前 action lambda 中返回,跳过后续语句,继续下一轮调用,起到了continue的作用

转换为java代码验证:

java 复制代码
Integer[] $this$forEach$iv = new Integer[]{1, 2, 3, 4};
Iterable $this$forEach$iv = (Iterable)CollectionsKt.listOf($this$forEach$iv);
int $i$f$forEach = 0;

for(Object element$iv : $this$forEach$iv) {
   int it = ((Number)element$iv).intValue();
   int var6 = 0;
   if (it != 2) {
      System.out.println(it);
   }
}

等价于continue,只跳过了it == 2

java 复制代码
listOf(1, 2, 3, 4).forEach {
    if (it == 2) return
    println(it)
}

那么改为return呢

java 复制代码
Integer[] $this$forEach$iv = new Integer[]{1, 2, 3, 4};
Iterable $this$forEach$iv = (Iterable)CollectionsKt.listOf($this$forEach$iv);
int $i$f$forEach = 0;

for(Object element$iv : $this$forEach$iv) {
 int it = ((Number)element$iv).intValue();
 int var6 = 0;
 if (it == 2) {
    return;
 }

 System.out.println(it);
}

整个方法结束运行

多层 forEach 中跳出一层循环

java 复制代码
fun test() {
    listOf(1, 2).forEach outer@ { i ->
        listOf("A", "B").forEach { j ->
            if (j == "B") return@outer  // 跳出当前 i 的整轮内层循环,进入下一个 i
            println("i = $i, j = $j")
        }
    }
}

说明:结束外层 forEach outer@ 当前这一次 lambda 执行,跳出它对应的代码块,进入下一次迭代。

转换为java代码验证:

java 复制代码
for(Object element$iv : $this$forEach$iv) {
   int i = ((Number)element$iv).intValue();
   int var6 = 0;
   String[] $this$forEach$iv = new String[]{"A", "B"};
   Iterable $this$forEach$iv = (Iterable)CollectionsKt.listOf($this$forEach$iv);
   int $i$f$forEach = 0;

   for(Object element$iv : $this$forEach$iv) {
      String j = (String)element$iv;
      int var12 = 0;
      if (Intrinsics.areEqual(j, "B")) {
         break;
      }

      String var13 = "i = " + i + ", j = " + j;
      System.out.println(var13);
   }
}

多层 lambda 中跳出外层块

java 复制代码
fun test() {
    run outer@ {
        listOf(1, 2, 3).forEach { i ->
            listOf("A", "B").forEach { j ->
                if (i == 2 && j == "B") return@outer  // 跳出 run@outer
                println("i = $i, j = $j")
            }
        }
    }
    println("done")
}

说明:return@outer 会跳出整个 run outer@ { ... } 的 lambda 表达式块

转换为java代码验证:

java 复制代码
TestReturn $this$test_u24lambda_u242 = this;
int var3 = 0;
Integer[] $this$forEach$iv = new Integer[]{1, 2, 3};
Iterable $this$forEach$iv = (Iterable)CollectionsKt.listOf($this$forEach$iv);
int $i$f$forEach = 0;

label24:
for(Object element$iv : $this$forEach$iv) {
   int i = ((Number)element$iv).intValue();
   int var9 = 0;
   String[] $this$forEach$iv = new String[]{"A", "B"};
   Iterable $this$forEach$iv = (Iterable)CollectionsKt.listOf($this$forEach$iv);
   int $i$f$forEach = 0;

   for(Object element$iv : $this$forEach$iv) {
      String j = (String)element$iv;
      int var15 = 0;
      if (i == 2 && Intrinsics.areEqual(j, "B")) {
         break label24;
      }

      String var16 = "i = " + i + ", j = " + j;
      System.out.println(var16);
   }
}

String var1 = "done";
System.out.println(var1);

等价于java的break label,跳出外层循环。

示例输出:

java 复制代码
i = 1, j = A
i = 1, j = B
i = 2, j = A
done

with, run, let 等内联表达式中提前结束

java 复制代码
fun test() {
    val result = run label@ {
        val x = getSomeValue()
        if (x == null) return@label "default" // 提前退出表达式块
        println("x is not null")
        "value is $x"
    }
    println(result)
}

说明:return@label 退出 run 表达式并返回值

转换为java代码验证:

java 复制代码
public final void test() {
   TestReturn $this$test_u24lambda_u240 = this;
   int var4 = 0;
   String x = $this$test_u24lambda_u240.getSomeValue();
   String var10000;
   if (x == null) {
      var10000 = "default";
   } else {
      String var6 = "x is not null";
      System.out.println(var6);
      var10000 = "value is " + x;
   }

   String result = var10000;
   System.out.println(result);
}
相关推荐
雨白39 分钟前
StateFlow 与 SharedFlow:在协程中管理状态与事件
android·kotlin
WAsbry2 小时前
NFC开发系列专栏 - 第三篇:无界面NFC后台服务方案
android·程序员·架构
消失的旧时光-19432 小时前
WebView 最佳封装模板(BaseWebActivity + WebViewHelper)
android·webview
WAsbry2 小时前
NFC开发系列-第一篇:NFC开发基础与实战入门
android·程序员
WAsbry2 小时前
NFC开发系列 - 第二篇:NFC企业级架构设计与最佳实践
android·程序员·架构
feibafeibafeiba3 小时前
Android 14 关于imageview设置动态padding值导致图标旋转的问题
android
tangweiguo030519874 小时前
ProcessLifecycleOwner 完全指南:优雅监听应用前后台状态
android·kotlin
介一安全5 小时前
【Frida Android】基础篇15(完):Frida-Trace 基础应用——JNI 函数 Hook
android·网络安全·ida·逆向·frida
吞掉星星的鲸鱼6 小时前
android studio创建使用开发打包教程
android·ide·android studio
陈老师还在写代码6 小时前
android studio 签名打包教程
android·ide·android studio