Kotlin-Returns and Jumps


标签: Kotlin

本文翻译自如官方文档:Returns and Jumps——如有疏漏和错误,欢迎指正!
此外额外增加一些知识点以及实例。


  • Kotlin-Returns and Jumps
  • 1-返回和跳转
  • 1-Break和Continue标签
  • 2-在标签处的Return
  • 3-实例讲解
  • 1-Returns and Jumps
  • 1-Break and Continue Labels
  • 2-Return at Labels


1-返回和跳转


Kotlin有三种结构跳转表达式:

  • return. 默认从最近的封闭方法(包含该return的方法)或者匿名方法返回。
  • break. 终止最近的封闭循环。
  • continue. 从最近的封闭循环的下一步继续下去。

这些表达式都可以被用作更大的表达式的一部分:

val s = person.name ?: return

这些表达式的类型是Nothing类型
The type of these expressions is the Nothing type.

1-Break和Continue标签

在Kotlin中的任何表达式可以用label标签来标记。标签有一种描述符格式-尾随一个@标志。例如:abc@, fooBar@就是合法标签(参照grammar语法)。为了标记一个表达式,我们将标签放置于该表达式的前面:

loop@ for (i in 1..100) {
    // ...
}

现在,我们可以使用标签来标记一个break或者continue:

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (...) break@loop //直接结束loop@标记的for循环
    }
}

使用标签标记的break跳转到拥有该标签的循环体的下一执行点去执行(该循环结束)。continue会从该循环的下一次开始,继续执行下去。

2-在标签处的Return

通过函数字面值、本地函数和对象表达式,函数可以在Kotlin中被嵌套起来。标记过的returns允许我们从外部函数返回。这最重要的使用方法是从一个lambda表达式返回。如下:

fun foo() {
    ints.forEach { //ints是一个int数组
        if (it == 0) return  //非本地的return,从内部的lambda直接返回到foo()的调用者
        print(it)
    }
}

这个return-表达式从最近的封闭函数返回。例如foo(注意:只有传递给内联函数的lambda表达式才支持这种非局部returns)。

lambda表达式:lambda是一种函数式编程,最大特征是函数可以作为其他函数的参数。lambda本质是是匿名方法,编译器会自动将lambda表达式转换为“匿名方法”。
这里的ints.forEach{…}就是lambda表达式:在int数组ints执行的forEach方法中,将{…}中的全部内容,作为forEach方法的参数。

如果我们想从一个lambda表达式返回,我们不得不用标签标记这个return:

fun foo() {
    ints.forEach lit@ {
        if (it == 0) return@lit
        print(it)
    }
}

上例,它仅从lambda表达式return。有时更方便使用隐式标签:该标签有和函数相同的名字。

fun foo() {
    ints.forEach {
        if (it == 0) return@forEach
        print(it)
    }
}

另一方面,我们可以用匿名函数替换lambda表达式。在匿名函数的return语句将从匿名函数本身返回。

fun foo() {
    ints.forEach(fun(value: Int) {
        if (value == 0) return  // 从匿名函数的调用者局部返回,即forEach循环。
        print(value)
    })
}

当正在返回一个值,解析器会优先考虑标签化返回:

return@a 1

意思是“return 1 到标签@处”,而不是”返回一个标签表达式(@a 1)

3-实例讲解:

该实例是完成打印数组内容的功能。

方法fooNoLabel没有使用标签return,是直接return到fooNoLabel调用者,因此只打印出1,2,3。

fooWithLabel使用标签label@,return是返回到forEach循环,因此打印出1,2,3,5,打印出所有不为0的数字。

fun main(args: Array<String>) {
    val intarray: IntArray = intArrayOf(1, 2, 3, 0, 5)
    fooNoLabel(intarray) //结果 1, 2, 3
    fooWithLabel(intarray) //结果 1, 2, 3, 5
}

fun fooNoLabel(ints: IntArray) {
    ints.forEach{
        if (it == 0) return 
        println(it)
    }
}

fun fooWithLabel(ints: IntArray) {
    ints.forEach label@{
        if (it == 0) return@label  
        println(it)
    }
}

如下是官方文档原文:Returns and Jumps

1-Returns and Jumps

Kotlin has three structural jump expressions:

  • return. By default returns from the nearest enclosing function or anonymous function.
  • break. Terminates the nearest enclosing loop.
  • continue. Proceeds to the next step of the nearest enclosing loop.

All of these expressions can be used as part of larger expressions:

val s = person.name ?: return

The type of these expressions is the Nothing type.

1-Break and Continue Labels

Any expression in Kotlin may be marked with a label. Labels have the form of an identifier followed by the @ sign, for example: abc@, fooBar@ are valid labels (see the grammar). To label an expression, we just put a label in front of it

loop@ for (i in 1..100) {
    // ...
}

Now, we can qualify a break or a continue with a label:

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (...) break@loop
    }
}

A break qualified with a label jumps to the execution point right after the loop marked with that label. A continue proceeds to the next iteration of that loop.

2-Return at Labels

With function literals, local functions and object expression, functions can be nested in Kotlin. Qualified returns allow us to return from an outer function. The most important use case is returning from a lambda expression. Recall that when we write this:

fun foo() {
    ints.forEach {
        if (it == 0) return  // nonlocal return from inside lambda directly to the caller of foo()
        print(it)
    }
}

The return-expression returns from the nearest enclosing function, i.e. foo. (Note that such non-local returns are supported only for lambda expressions passed to inline functions.) If we need to return from a lambda expression, we have to label it and qualify the return:

fun foo() {
    ints.forEach lit@ {
        if (it == 0) return@lit
        print(it)
    }
}

Now, it returns only from the lambda expression. Oftentimes it is more convenient to use implicits labels: such a label has the same name as the function to which the lambda is passed.

fun foo() {
    ints.forEach {
        if (it == 0) return@forEach
        print(it)
    }
}

Alternatively, we can replace the lambda expression with an anonymous function. A return statement in an anonymous function will return from the anonymous function itself.

fun foo() {
    ints.forEach(fun(value: Int) {
        if (value == 0) return  // local return to the caller of the anonymous fun, i.e. the forEach loop
        print(value)
    })
}

When returning a value, the parser gives preference to the qualified return, i.e.

return@a 1

means “return 1 at label @a” and not “return a labeled expression (@a 1)“.