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);
}
相关推荐
进击的cc12 分钟前
面试官:Handler 没消息时为啥不卡死?带你从源码到底层内核彻底整明白!
android·面试
Yang-Never23 分钟前
OpenGL ES ->YUV图像基础知识
android·java·开发语言·kotlin·android studio
2501_9159214342 分钟前
常用iOS性能测试工具大全及使用指南
android·测试工具·ios·小程序·uni-app·cocoa·iphone
晨尘光43 分钟前
【Android下载gradle超时解决方法】
android
zhen_hong1 小时前
ReactAgent原理
android·java·javascript
皮皮宋吖1 小时前
皮皮宋渗透日记 11|文件包含漏洞全解析:LFI/RFI/ 伪协议 / 绕过 / 防御
android·安全
阿拉斯攀登1 小时前
【无人售货柜・RK+YOLO】篇 6:安卓端落地!RK3576 + 安卓系统,YOLO RKNN 模型实时推理保姆级教程
android·人工智能·yolo·目标跟踪·瑞芯微·嵌入式驱动
只能是遇见1 小时前
ERROR 1524 (HY000) Plugin ‘mysql_native_password‘ is not loaded
android·数据库·mysql
helloworddm2 小时前
第一篇:设计模式在 Android 视频播放器中的实战应用
android·设计模式·音视频
恋猫de小郭2 小时前
Android 禁止侧载将正式实施,需要等待 24 小时冷静期
android·flutter·harmonyos