Lambdas and Higher-Order Functions
Prerequisites: Default and named arguments
A lambda is a function without a name, written inline, that you can store in a variable or pass to another function. A function that takes or returns another function is a higher-order function.
Real-world analogy: a lambda is an instruction slip you hand to someone along with a job — "for each parcel, check the weight like this". They decide when to follow it.
Syntax
fun main() {
val double = { n: Int -> n * 2 }
val add: (Int, Int) -> Int = { a, b -> a + b }
println(double(21))
println(add(2, 3))
}
42
5
- Parameters come before
->; the body after it. - The last expression in the body is the result — no
return. (Int, Int) -> Intis a function type: "takes twoInts, returns anInt".
it
When a lambda has exactly one parameter, you can leave it out and refer
to it as it:
fun main() {
val shout: (String) -> String = { it.uppercase() + "!" }
println(shout("hi"))
}
HI!
Passing lambdas to functions
If a function's last parameter is a function, the lambda can go outside the parentheses — a trailing lambda. If it's the only argument, the parentheses can go entirely:
fun repeatTask(times: Int, task: (Int) -> Unit) {
for (i in 1..times) task(i)
}
fun main() {
repeatTask(3) { pass ->
println("Pass $pass")
}
val names = listOf("Linus", "ada", "Grace")
println(names.sortedBy { it.lowercase() })
}
Pass 1
Pass 2
Pass 3
[ada, Grace, Linus]
Trailing lambdas are everywhere in Kotlin — collection functions, Android's Jetpack Compose, Gradle build files — which is why Kotlin code so often reads like a built-in language feature.
Function references
:: refers to an existing function, so you can pass it like a lambda:
fun isLong(word: String) = word.length > 4
fun main() {
val words = listOf("fig", "banana", "kiwi", "cherry")
println(words.filter(::isLong))
}
[banana, cherry]
Capturing variables
Lambdas can read and change variables from the surrounding code:
fun main() {
var total = 0
listOf(5, 10, 15).forEach { total += it }
println(total)
}
30
Common mistakes
- Writing
returninside a lambda passed toforEachand expecting it to skip one item — a plainreturnreturns from the whole enclosing function. Usereturn@forEachto skip to the next item. - Overusing
itin nested lambdas, where it becomes unclear whichitis which. Name the parameters instead.
Practice
Sort listOf("banana", "fig", "apple") by word length, shortest first,
using a trailing lambda.
Show solution
fun main() {
val words = listOf("banana", "fig", "apple")
println(words.sortedBy { it.length })
}
[fig, apple, banana]