Avatar ·

Как передать функцию в качестве параметра в другую функцию в Kotlin?

Привет! Я только начал изучать Kotlin после Java, и у меня возникла проблема. В Java я привык передавать функции через интерфейсы или лямбды, но в Kotlin всё как-то иначе. Допустим, у меня есть простая функция, которая складывает два числа: ```kotlin fun add(a: Int, b: Int): Int { return a + b } ``` И я хочу передать её в другую функцию, которая будет применять её к паре чисел и выводить результат. Я попробовал написать что-то вроде: ```kotlin fun applyOperation(x: Int, y: Int, operation: ???) { val result = operation(x, y) println("Результат: $result") } ``` Но не знаю, какой тип указать для параметра `operation`. Я слышал что-то про `Function` или `(Int, Int) -> Int`, но не уверен, как это правильно использовать. Также интересно, можно ли передавать функции с разными сигнатурами, например, с разным количеством параметров или возвращаемыми типами. Подскажите, как это делается в Kotlin? Примеры кода очень помогут. Спасибо! <<>>

Использование функциональных типов в Kotlin

В Kotlin функции являются объектами первого класса, и их можно передавать как параметры с помощью функциональных типов. Для вашего примера правильный тип — (Int, Int) -> Int, что означает: "функция, принимающая два Int и возвращающая Int".

fun applyOperation(x: Int, y: Int, operation: (Int, Int) -> Int) {
    val result = operation(x, y)
    println("Результат: $result")
}

// Использование
val add = { a: Int, b: Int -> a + b }
applyOperation(5, 3, add) // Результат: 8

// Или лямбда напрямую
applyOperation(10, 4) { a, b -> a * b } // Результат: 40

Передача именованной функции

Вы также можете передавать уже определённые функции через ссылку с оператором :::

fun multiply(a: Int, b: Int) = a * b
applyOperation(6, 2, ::multiply) // Результат: 12

Работа с разными сигнатурами

Для функций с другим количеством параметров или возвращаемыми типами используйте соответствующие функциональные типы:

  • () -> Unit — функция без параметров, ничего не возвращает
  • (String) -> Boolean — принимает строку, возвращает Boolean
  • (Int, Double, String) -> List<Int> — три параметра разных типов, возвращает список
fun executeWithLog(action: () -> Unit) {
    println("Начало выполнения")
    action()
    println("Конец выполнения")
}

executeWithLog { println("Привет!") }
// Вывод:
// Начало выполнения
// Привет!
// Конец выполнения

Функции с nullable-типом

Если функция может быть null, оберните тип в скобки и добавьте вопросительный знак:

fun safeOperation(x: Int, y: Int, operation: ((Int, Int) -> Int)?) {
    val result = operation?.invoke(x, y) ?: 0
    println("Результат: $result")
}

safeOperation(5, 3, null) // Результат: 0

Практический пример: фильтрация списка

fun filterList(list: List<Int>, predicate: (Int) -> Boolean): List<Int> {
    val result = mutableListOf<Int>()
    for (item in list) {
        if (predicate(item)) result.add(item)
    }
    return result
}

val numbers = listOf(1, 2, 3, 4, 5, 6)
val evenNumbers = filterList(numbers) { it % 2 == 0 }
println(evenNumbers) // [2, 4, 6]

Дополнительные советы

  • Для коротких лямбд используйте неявный параметр it, если функция принимает один аргумент
  • Если лямбда — последний параметр, её можно вынести за скобки (trailing lambda)
  • Для многострочных лямбд последнее выражение становится возвращаемым значением

Такой подход делает код гибким и читаемым, позволяя легко абстрагировать поведение.

<<>>

Использование typealias для удобства

Если вы часто используете один и тот же функциональный тип, можно задать для него псевдоним с помощью typealias. Это улучшает читаемость кода, особенно в сложных сигнатурах.

typealias IntOperation = (Int, Int) -> Int

fun applyOperation(x: Int, y: Int, operation: IntOperation) {
    println(operation(x, y))
}

fun subtract(a: Int, b: Int) = a - b
applyOperation(10, 3, ::subtract) // 7

Функции с несколькими параметрами-функциями

Вы можете передавать несколько функций разного типа:

fun combineResults(
    a: Int, 
    b: Int, 
    transform: (Int) -> Int,
    combine: (Int, Int) -> Int
): Int {
    return combine(transform(a), transform(b))
}

val result = combineResults(3, 4, { it * 2 }, { x, y -> x + y })
println(result) // (3*2) + (4*2) = 14

Inline-функции для оптимизации

Если вы передаёте лямбды в функции высшего порядка, Kotlin создаёт анонимные классы, что может повлиять на производительность. Для критичных участков используйте модификатор inline:

inline fun measureTime(action: () -> Unit) {
    val start = System.nanoTime()
    action()
    val end = System.nanoTime()
    println("Время: ${end - start} ns")
}

measureTime { 
    Thread.sleep(100) 
}

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