Возврат и прыжки в Kotlin

Возврат и прыжки

В Kotlin есть три выражения структурного скачка:

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

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

val s = person.name ?: return

Тип этих выражений - тип Nothing.

Break и Continue метки

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

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

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

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

Вывод:

12

Возвращаемое выражение возвращается из ближайшей включающей функции, то есть foo. (Обратите внимание, что такие нелокальные возвраты поддерживаются только для лямбда-выражений, передаваемых встроенным функциям.) Если нам нужно вернуться из лямбда-выражения, мы должны пометить его и квалифицировать возврат:

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

Вывод:

1245 сделано с явной меткой

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

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

Вывод:

1245 сделано с неявной меткой

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

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

Вывод:

1245 сделано с анонимной функцией

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

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

Вывод:

12 сделано с вложенным циклом

При возврате значения парсер отдает предпочтение квалифицированному возврату, т.е.

return@a 1

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


Читайте также:


Комментарии

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

Строки в Kotlin

Наследование в Kotlin