Операторы перехода
В 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
, отмеченный @loop
, переводит выполнение кода в точку сразу после цикла, отмеченного этой меткой.
Оператор continue
продолжает цикл со следующей его итерации.
Возврат к меткам
В Kotlin функции могут быть вложены друг в друга с помощью литералов функций, локальных функций и анонимных объектов.
Подходящий return
позволит вернуться из внешней функции.
Одним из самых важных применений этой синтаксической конструкции является возврат из лямбда-выражения.
Напомним, что в таких случаях, как в примере ниже, return
возвращает из ближайшей заключающей функции - foo
:
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // нелокальный возврат, непосредственно к объекту вызывающему функцию foo()
print(it)
}
println("эта строка не достижима")
}
Обратите внимание, что такой нелокальный возврат поддерживается только лямбда-выражениями, переданными инлайн-функциям.
Чтобы вернуться из лямбда-выражения, к оператору стоит поставить метку и тем самым сделать уточнение для 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)
”.