Функции, лямбды и функции высшего порядка

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

Функция объявляется ключевым словом fun. Дальше идут имя, параметры с типами и, при необходимости, тип возвращаемого значения после двоеточия. Если функция короткая и возвращает одно выражение, её можно записать через знак равенства — без фигурных скобок и без return.

fun sum(a: Int, b: Int): Int {
    return a + b
}

// то же самое в краткой форме (expression body)
fun sumShort(a: Int, b: Int) = a + b

println(sum(2, 3))       // 5
println(sumShort(2, 3))  // 5

Параметры по умолчанию и именованные аргументы

В Java ради разных наборов параметров писали перегрузки. В Kotlin достаточно задать значение по умолчанию. А при вызове можно указывать аргументы по именам — это резко повышает читаемость, особенно когда параметров много.

fun greet(name: String, greeting: String = "Привет"): String {
    return "$greeting, $name!"
}

println(greet("Аня"))                       // Привет, Аня!
println(greet("Аня", greeting = "Здравствуй")) // именованный аргумент

Лямбды и функции высшего порядка

Лямбда — это анонимная функция, записанная в фигурных скобках. Функция высшего порядка — это функция, которая принимает другую функцию параметром или возвращает её. Именно на этом построены все операции над коллекциями и весь декларативный стиль Compose.

// функция высшего порядка: принимает операцию как параметр
fun calculate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
    return op(a, b)
}

val res1 = calculate(4, 5) { x, y -> x + y }  // лямбда сложения
val res2 = calculate(4, 5) { x, y -> x * y }  // лямбда умножения
println(res1)  // 9
println(res2)  // 20

Как работает под капотом

Тип функции записывается стрелкой: (Int, Int) -> Int означает «функция, принимающая два Int и возвращающая Int». Лямбда — это объект такого типа. Если лямбда — последний аргумент функции, её выносят за круглые скобки (trailing lambda) — отсюда привычный синтаксис list.filter { ... }. Если у лямбды один параметр, его можно не называть и обращаться к нему через неявное имя it.

  Тип функции:  (Int, Int) -> Int
                 ^^^^^^^^     ^^^
                 параметры   результат

  Trailing lambda:
  calculate(4, 5) { x, y -> x + y }
                  \_______________/
                   вынесена за скобки

Частые ошибки

Забывать про it. В лямбде с одним параметром не обязательно писать { item -> item.length } — короче { it.length }. Но если лямбд вложено несколько, лучше назвать параметры явно, чтобы не запутаться.

Писать длинные лямбды. Если тело лямбды разрослось, вынесите логику в отдельную именованную функцию — читать станет легче.

Игнорировать параметры по умолчанию. Новички плодят перегрузки, хотя одно значение по умолчанию решает задачу элегантнее.

Best practices

  • Короткие функции пишите через expression body со знаком равенства.
  • Используйте параметры по умолчанию вместо множества перегрузок.
  • Именованные аргументы — для вызовов с большим числом параметров.
  • Лямбды держите короткими; сложную логику выносите в именованные функции.

Идею функции высшего порядка удобно увидеть на Python: функция принимает операцию параметром, как calculate в Kotlin. Запустите врезку.

# Аналог функции высшего порядка из Kotlin
def calculate(a, b, op):
    return op(a, b)

print(calculate(4, 5, lambda x, y: x + y))  # 9
print(calculate(4, 5, lambda x, y: x * y))  # 20
print(calculate(10, 3, lambda x, y: x - y)) # 7

Попробуй сам ▶ — передайте свою операцию (деление, остаток, максимум). В Kotlin это была бы лямбда { x, y -> ... }.

Закрепим главное

Закрепите идею функции как значения. В Kotlin функцию можно передать в другую функцию, вернуть из неё и хранить в переменной — ровно как число или строку. Это не академическая деталь, а рабочий инструмент: именно так устроены и операции над коллекциями (filter { ... }), и обработчики событий в Compose (onClick = { ... }). Освоив лямбды сейчас, вы заранее понимаете синтаксис, который встретите на каждом экране Android-приложения.

Второй ориентир — лаконичность через параметры по умолчанию и именованные аргументы. Они заменяют ворох перегрузок одной гибкой сигнатурой и делают вызовы самодокументируемыми: greet(name, greeting = "Здравствуй") читается без обращения к документации. Это особенно ценно в Compose, где у composable бывает много параметров, и именованные аргументы спасают от путаницы, какой из них за что отвечает.

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

Проверьте себя
1. Что такое функция высшего порядка?
AФункция, объявленная вне класса
BФункция, которая принимает другую функцию параметром или возвращает функцию
CФункция, которая выполняется быстрее обычной
DФункция без параметров
2. Как обратиться к единственному параметру лямбды, не называя его?
AЧерез ключевое слово this
BЧерез имя arg0
CЧерез неявное имя it
DНикак, параметр обязательно нужно называть