ПрограммированиеKotlin разработчик

Как в Kotlin реализуются функции высшего порядка и лямбда-выражения? Опишите нюансы передачи и возвращения функций, особенности синтаксиса, основные ограничения и приведите примеры кода.

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Функции высшего порядка — это функции, которые принимают другие функции в качестве параметров или возвращают их. Kotlin использует лямбда-выражения, чтобы удобно передавать поведение как значение.

Пример объявления:

fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b) } val sum = operateOnNumbers(3, 2) { x, y -> x + y } // sum = 5

Передача функций:

  • Функции можно передавать не только в виде лямбда-выражения, но и ссылкой: :
fun multiply(x: Int, y: Int) = x * y operateOnNumbers(2, 3, ::multiply)

Возврат функции:

fun makeMultiplier(factor: Int): (Int) -> Int = { x -> x * factor } val triple = makeMultiplier(3) val result = triple(10) // 30

Особенности:

  • Лямбда может иметь не более одного неименованного параметра (it).
  • К функции можно передать named параметры, но типы должны быть явно указаны, если не удаётся вывести тип.
  • Лямбда-выражения — это объекты (анонимные классы), что влияет на производительность при большом количестве вызовов в горячих циклах (решается inline-функциями).
  • Для лямбда с захватами переменных используется closure (замыкание).

Вопрос с подвохом.

Какая разница между объявлением типа функции (Int, Int) -> Int и использованием типа Function2<Int, Int, Int>?

Ответ: Синтаксис (Int, Int) -> Int просто более "красивое" объявление (синтаксический сахар) для интерфейса Function2<Int, Int, Int>. На практике оба варианта полностью взаимозаменяемы.

val f1: (Int, Int) -> Int = { x, y -> x + y } val f2: Function2<Int, Int, Int> = { x, y -> x + y }

Но первый вариант обычно предпочтителен из-за читаемости.

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В крупной системе обработки событий создавались десятки лямбда-выражений внутри цикла без использования inline-функции. Это вызвало высокую нагрузку на GC и деградацию производительности, т.к. для каждого вызова создавался отдельный анонимный объект функции.


История

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


История

Разработчик попытался использовать лямбду без явно указываемого типа в качестве параметра другой функции с невыведенным явно типом, что привело к ошибке "cannot infer a type for this parameter". Проблема решилась явным указанием типа лямбды или параметра функции.