在 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
类型的 lambdareturn@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);
}