Lambdas and Higher-Order Functions
Passing behavior as a value — anonymous functions and the functions that accept them as parameters.
អាន 3 នាទី
In Kotlin, functions are values — you can store one in a variable, pass one as an argument, or return one from another function, the same as you would with a String or an Int. A lambda is the most common way to write such a function: a small, unnamed block of code you can pass around directly.
Lambda syntax
A lambda is written inside curly braces, with parameters (if any) before an arrow ->:
val square: (Int) -> Int = { x -> x * x }
println(square(5)) // 25The type (Int) -> Int reads as "a function that takes an Int and returns an Int." You can also let Kotlin infer that type from context, which is far more common in practice:
val square = { x: Int -> x * x }Higher-order functions
A function that takes another function as a parameter (or returns one) is called a higher-order function. This is where lambdas earn their keep:
fun applyTwice(x: Int, operation: (Int) -> Int): Int {
return operation(operation(x))
}
val result = applyTwice(3) { it * 2 }
println(result) // 12 — (3 * 2) * 2Two things are worth noticing here. First, it — when a lambda has exactly one parameter and its type is clear from context, Kotlin lets you refer to it as it instead of naming it. Second, the lambda is written outside the parentheses: when a function's last parameter is a lambda, Kotlin lets you move it after the closing ) — or, as here, drop the parentheses entirely when the lambda is the only argument. This is called trailing lambda syntax, and it's why Kotlin code that builds UI or configures something often looks like it's using its own mini-language:
button.setOnClickListener {
println("Button clicked!")
}That reads almost like a built-in language feature, but it's really just a regular function (setOnClickListener) taking a lambda as its last argument, written with trailing lambda syntax.
Why this matters: behavior as data
Higher-order functions let you separate what varies from what stays the same. Consider filtering a list two different ways without lambdas — you'd need two nearly identical loops. With a higher-order function, the loop is written once, and the varying part is passed in:
fun <T> customFilter(items: List<T>, predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in items) {
if (predicate(item)) result.add(item)
}
return result
}
val numbers = listOf(1, 2, 3, 4, 5, 6)
val evens = customFilter(numbers) { it % 2 == 0 }
val bigOnes = customFilter(numbers) { it > 4 }
println(evens) // [2, 4, 6]
println(bigOnes) // [5, 6]customFilter doesn't know or care what condition it's checking — that decision is entirely up to whatever lambda gets passed in at the call site.
This exact pattern — a function that loops, plus a lambda that decides what to do with each element — is precisely how Kotlin's built-in collection functions like filter, map, and reduce work, which is exactly what the next lesson covers.