Операторы перехода
В Kotlin определено три оператора перехода:
- return по умолчанию производит возврат из ближайшей окружающей его функции или анонимной функции
- break завершает выполнение цикла
- continue продолжает выполнение цикла со следующего его шага, без обработки оставшегося кода текущей итерации
Метки операторов 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, отмеченный @loop
, переводит выполнение кода к той его части, которая находится сразу после соответствующей метки loop@
.
Оператор continue продолжает цикл со следующей его итерации.
Возврат к меткам
В Kotlin функции могут быть вложены друг в друга с помощью анонимных объектов, локальных функций (ориг.:local functions) и function literals. Подходящий return позволит вернуться из внешней функции. Одним из самых удачных применений этой синтаксической конструкции служит возврат из лямбда-выражения. Подумайте над этим утверждением, читая данный пример:
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // нелокальный возврат, непосредственно к объекту вызывающему функцию foo()
print(it)
}
println("эта строка не достижима")
}
Опертор return возвращается из ближайшей функции, в нашем случае foo
.
(Обратите внимание, что такой местный возврат поддерживается только лямбда-выражениями, переданными инлайн-функциям.) Если нам надо вернуться из лямбда-выражения, к оператору стоит поставить метку и тем самым сделать уточнение для ключевого слова return:
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // локальный возврат внутри лямбды, то есть к циклу forEach
print(it)
}
print(" выполнится с использованием явной метки(lit@)")
}
Теперь он возвращается только из лямда-выражения. Зачастую намного более удобно указывать метки неявно: такие метки имеют такое же имя, как и функция, к которой относится лямбда.
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // локальный возврат внутри лямбды, то есть к циклу forEach
print(it)
}
print(" выполнится с использованием неявной метки(forEach@)")
}
Возможно также использование анонимной функции в качестве альтернативы лямбда-выражениям. Оператор return возвращается из самой анонимной функции.
fun foo() {
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // локальный возврат внутри анонимной функци, то есть к циклу forEach
print(value)
})
print(" выполнится с использованием анонимной функции")
}
Обратите внимание, что использование локальных возвратов в предыдущих трех примерах аналогично использованию continue в обычных циклах. Прямого эквивалента для break не существует, но его можно смоделировать - добавить еще одну вложенную лямбду и нелокально вернуться из нее:
fun foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // нелокальный возврат из лямбды к вызывающему run
print(it)
}
}
print(" выполнится с использованием вложенной метки")
}
При возвращении значения парсер отдаёт предпочтение специализированному возврату, типа
return@a 1
что значит "верни 1
в метке @a
, а не "верни выражение с меткой (@a 1)
".