Операторы перехода

В Kotlin определено три структурных оператора перехода:

  • return по умолчанию производит возврат из ближайшей содержащей его функции или анонимной функции;
  • break завершает выполнение ближайшего окружающего его цикла;
  • continue продолжает выполнение цикла со следующего его шага, без обработки оставшегося кода текущей итерации.

Все эти выражения можно использовать как часть более крупных выражений:

val s = person.name ?: return

Эти выражения имеют тип Nothing.

Метки операторов break и continue

Любое выражение в Kotlin может быть помечено меткой label. Метки имеют форму идентификатора, за которым следует знак @, например abc@ или fooBar@. Для того чтобы пометить выражение, мы просто ставим метку перед ним.

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

Теперь можно уточнить операторы break или continue с помощью меток:

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

Оператор break, уточненный меткой, переводит выполнение кода в точку сразу после цикла, отмеченного этой меткой. Оператор continue переходит к следующей итерации этого цикла.

В некоторых случаях операторы break и continue можно применять нелокально, без явного определения меток. Такое нелокальное использование допустимо в лямбда-выражениях внутри окружающих инлайн-функций. {:.note}

Возврат к меткам

В Kotlin функции могут быть вложенными: для этого используются литералы функций, локальные функции и выражения объектов. Квалифицированный return позволяет вернуться из внешней функции.

Самый важный сценарий - возврат из лямбда-выражения. Чтобы вернуться из лямбда-выражения, пометьте его меткой и укажите эту метку у return:

//sampleStart
fun foo() {
    listOf(1, 2, 3, 4, 5).forEach lit@{
        if (it == 3) return@lit // локальный возврат к вызывающему коду лямбды - циклу forEach
        print(it)
    }
    print(" выполнено с явной меткой")
}
//sampleEnd

fun main() {
    foo()
}

{kotlin-runnable=“true” kotlin-min-compiler-version=“1.3”}

Теперь он возвращает только из лямбда-выражения. Зачастую намного более удобно использовать неявные метки, потому что такие метки имеют такое же имя, как и функция, к которой относится лямбда.

//sampleStart
fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return@forEach // локальный возврат к вызывающему коду лямбды - циклу forEach
        print(it)
    }
    print(" выполнено с неявной меткой")
}
//sampleEnd

fun main() {
    foo()
}

{kotlin-runnable=“true” kotlin-min-compiler-version=“1.3”}

В качестве альтернативы лямбда-выражению можно использовать анонимную функцию. Оператор return возвращает из самой анонимной функции.

//sampleStart
fun foo() {
    listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
        if (value == 3) return  // локальный возврат к вызывающему коду анонимной функции - циклу forEach
        print(value)
    })
    print(" выполнено с анонимной функцией")
}
//sampleEnd

fun main() {
    foo()
}

{kotlin-runnable=“true” kotlin-min-compiler-version=“1.3”}

Обратите внимание, что использование локальных возвратов в предыдущих трех примерах похоже на использование continue в обычных циклах.

Прямого эквивалента для break не существует, но его можно смоделировать: добавить внешнюю лямбду run и нелокально вернуться из нее.

//sampleStart
fun foo() {
    run loop@{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop // нелокальный возврат из лямбды, переданной в run
            print(it)
        }
    }
    print(" выполнено с вложенным циклом")
}
//sampleEnd

fun main() {
    foo()
}

{kotlin-runnable=“true” kotlin-min-compiler-version=“1.3”}

Нелокальный возврат здесь возможен, потому что вложенный вызов forEach() относится к инлайн-функции.

При возвращении значения парсер отдаёт предпочтение квалифицированному return:

return@a 1

Это означает “вернуть 1 по метке @a”, а не “вернуть помеченное выражение (@a 1)”.

В некоторых случаях из лямбда-выражения можно вернуть значение без меток. Такие нелокальные возвраты находятся в лямбде, но завершают окружающую инлайн-функцию. {:.note}