Функциональное программирование в Kotlin: основы и примеры

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в функциональное программирование на Kotlin

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

В этой статье мы разберём ключевые концепции ФП применительно к Kotlin: неизменяемость данных, функции как значения первого класса, лямбда-выражения, функции высшего порядка и цепочки обработки коллекций (map, filter, reduce). Все примеры будут сопровождаться практическим кодом, который можно сразу запустить в IntelliJ IDEA или онлайн-компиляторе.

Основы функционального подхода в Kotlin

Неизменяемость (Immutability)

Фундаментальный принцип ФП — работа с неизменяемыми данными. В Kotlin для этого используются ключевые слова val (неизменяемая ссылка) и неизменяемые коллекции (listOf, mapOf, setOf).

// Неизменяемый список
val numbers: List<Int> = listOf(1, 2, 3, 4, 5)

// Попытка изменить вызовет ошибку компиляции
// numbers.add(6) // Ошибка!

// Вместо мутации создаём новую коллекцию
val newNumbers = numbers + 6 // [1, 2, 3, 4, 5, 6]

Преимущества неизменяемости очевидны: код становится потокобезопасным, легче отлаживается, а побочные эффекты сводятся к минимуму. Если вам нужно изменить данные, вы всегда создаёте новую копию с нужными изменениями.

Функции как значения первого класса

В Kotlin функции — это полноценные объекты. Их можно присваивать переменным, передавать в другие функции и возвращать из функций. Тип функции записывается в круглых скобках со стрелкой:

// Функция как значение
val sum: (Int, Int) -> Int = { a, b -> a + b }

// Использование
val result = sum(10, 20) // 30

// Функция, принимающая другую функцию
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

println(operate(5, 3, sum)) // 8
println(operate(5, 3, { x, y -> x * y })) // 15

Обратите внимание: если лямбда является последним аргументом, Kotlin позволяет вынести её за скобки — это синтаксис trailing lambda, который делает код очень читаемым.

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

Лямбда-выражения

Лямбда — это анонимная функция, заключённая в фигурные скобки. В Kotlin лямбды могут захватывать переменные из внешнего контекста (замыкания).

// Простая лямбда
val greet = { name: String -> "Привет, $name!" }
println(greet("Анна")) // Привет, Анна!

// Лямбда с несколькими параметрами
val multiply: (Int, Int) -> Int = { a, b -> a * b }

// Если параметр один, можно использовать it
val double = { it: Int -> it * 2 }
println(double(5)) // 10

Функции высшего порядка (Higher-Order Functions)

Это функции, которые принимают другие функции как аргументы или возвращают их. В стандартной библиотеке Kotlin таких функций множество: let, apply, run, with и другие. Рассмотрим практический пример:

data class User(val name: String, val age: Int)

fun processUsers(users: List<User>, filter: (User) -> Boolean, transform: (User) -> String): List<String> {
    return users.filter(filter).map(transform)
}

val users = listOf(
    User("Иван", 25),
    User("Мария", 17),
    User("Пётр", 30)
)

// Отфильтруем совершеннолетних и выведем их имена
val adultNames = processUsers(
    users,
    filter = { it.age >= 18 },
    transform = { "Взрослый: ${it.name}" }
)

println(adultNames) // [Взрослый: Иван, Взрослый: Пётр]

Функция processUsers — это чистая функция высшего порядка, которая ничего не знает о конкретной логике фильтрации или трансформации. Это делает её максимально переиспользуемой.

Работа с коллекциями: map, filter, reduce

Одна из сильнейших сторон функционального подхода в Kotlin — цепочки операций над коллекциями. Вместо циклов и изменяемых переменных вы используете декларативные преобразования.

map — преобразование элементов

val numbers = listOf(1, 2, 3, 4, 5)

// Увеличим каждый элемент в 10 раз
val multiplied = numbers.map { it * 10 }
println(multiplied) // [10, 20, 30, 40, 50]

// Преобразуем в строки
val strings = numbers.map { "Число $it" }
println(strings) // [Число 1, Число 2, Число 3, Число 4, Число 5]

filter — фильтрация по условию

val numbers = listOf(1, 2, 3, 4, 5, 6)

// Оставим только чётные
val evens = numbers.filter { it % 2 == 0 }
println(evens) // [2, 4, 6]

// Фильтрация с индексом
val indexed = numbers.filterIndexed { index, value -> index > 2 && value > 3 }
println(indexed) // [4, 5, 6]

reduce — свёртка коллекции в одно значение

val numbers = listOf(1, 2, 3, 4, 5)

// Сумма всех элементов
val sum = numbers.reduce { acc, i -> acc + i }
println(sum) // 15

// Произведение с начальным значением (fold)
val product = numbers.fold(1) { acc, i -> acc * i }
println(product) // 120

Разница

Блоги

Рекомендаций по книгам