Возвращение значения и переходы

В Kotlin имеется три выражения структурного перехода:

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

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

val s = person.name ?: return

тип этих выражений является тип Nothing.

Метки break и continue

Либое выражение в Kotlin может быть отмечено меткой. Метки имееют форму идентификатора после которого следует символ @. Например bc@, 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 переходит к следующей итерации этого цикла.

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

С помощью функциональных литералов, локальных функций и object выражений, в Kotlin языке функции могут быть вложенными. return позволяет нам вернуться из внешней функции. Наиболее самым важным вариантом использования является возврат из лямбда-выражения. Об этом стоит помнить, когда мы пишем подобный код:

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return // не локальный возврат прямо к месту вызова foo()
        print(it)
    }
    println("this point is unreachable")
}

Выражение return возвращается к ближайщей функции, то есть к foo

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

Если нам нужно вернуться из лямбда выражения, мы должны отметить его меткой и указать в return

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

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

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

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

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
        if (value == 3) return  // локальный возврат к вызову анонимной функции, то есть к forEach циклу.
        print(value)
    })
    print(" done with anonymous function")
}

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

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

При возврате значения анализатор отдает предпочтение return@

return@a 1

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

Комментарии

Популярные сообщения из этого блога

Псевдонимы типов

Идиомы

Базовый синтаксис