Возвраты и переходы
В Kotlin есть три структурных выражения перехода:
- return. По умолчанию возвращает из ближайшей внешней функции или анонимной функции.
- break. Прерывает ближайший вложенный цикл.
- continue. Переходит к следующей итерации ближайшего вложенного цикла.
Все эти выражения могут использоваться как часть более сложных выражений:
val s = person.name ?: return
Тип этих выражений — тип Nothing.
Метки break и continue
Любое выражение в Kotlin может быть помечено меткой. Метки имеют вид идентификатора, за которым следует знак %, для примера: %, % являются допустимыми метками (см. грамматику). Чтобы пометить выражение, просто поместите метку перед ним.
loop@ for (i in 1..100) {
// ...
}
Теперь мы можем дополнить break или continue меткой:
loop@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@loop
}
}
Оператор break с меткой переходит к точке выполнения сразу после цикла, помеченного этой меткой. Оператор continue переходит к следующей итерации этого цикла.
Возврат с метками
С помощью лямбда-выражений, локальных функций и выражений объекта, функции могут быть вложены в Kotlin. Квалифицированные операторы return позволяют возвращаться из внешней функции. Наиболее важный случай — возврат из лямбда-выражения. Вспомните, как мы пишем это:
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // non-local return directly to the caller of foo()
print(it)
}
println("this point is unreachable")
}
//sampleEnd
fun main() {
foo()
}
Выражение return возвращает из ближайшей внешней функции, т.е. foo. (Обратите внимание, что такие возвраты из внешних функций поддерживаются только для лямбда-выражений, переданных в встроенные функции.) Если нам нужно вернуть значение из лямбда-выражения, мы должны пометить его и дополнить оператор return меткой:
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
print(it)
}
print(" done with explicit label")
}
//sampleEnd
fun main() {
foo()
}
Теперь он возвращает только из лямбда-выражения. Зачастую удобнее использовать неявные метки: такая метка имеет то же имя, что и функция, которой передаётся лямбда.
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // local return to the caller of the lambda, i.e. the forEach loop
print(it)
}
print(" done with implicit label")
}
//sampleEnd
fun main() {
foo()
}
В качестве альтернативы, мы можем заменить лямбда-выражение анонимной функцией. Оператор return в анонимной функции вернет из самой анонимной функции.
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // local return to the caller of the anonymous fun, i.e. the forEach loop
print(value)
})
print(" done with anonymous function")
}
//sampleEnd
fun main() {
foo()
}
Обратите внимание, что использование локальных возвратов в предыдущих трёх примерах аналогично использованию continue в обычных циклах. Прямого эквивалента для break нет, но его можно смоделировать, добавив ещё один вложенный лямбда-вызов и выполнив возврат из него:
//sampleStart
fun foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // non-local return from the lambda passed to run
print(it)
}
}
print(" done with nested loop")
}
//sampleEnd
fun main() {
foo()
}
При возврате значения анализатор отдаёт предпочтение квалифицированному возврату, т.е.
return@a 1
означает «возвратить 1 с меткой @a», а не «возвратить помеченное выражение (@a 1)».
© 2010–2020 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/returns.html