Lambdas power Kotlin's collection API, coroutines, DSLs and almost every modern Kotlin library.
A higher-order function takes or returns functions. Function types look like (Int, Int) -> Int or () -> Unit; nullable ones are ((String) -> Unit)?.
A lambda is written in braces: { a: Int, b: Int -> a + b }. Conventions that make Kotlin code read so smoothly:
- If a lambda has exactly one parameter, you can omit it and use
it:list.filter { it > 0 }. - Trailing lambdas: if the last parameter is a function, pass the lambda outside the parentheses:
repeat(3) { println(it) }. If it's the only argument, drop the parentheses entirely. - The last expression in a lambda is its return value;
returninside a lambda returns from the enclosing function only when the lambda is inlined, so use a label (return@forEach) to return from the lambda itself. - Unused parameters are written
_.
Function references pass existing functions: ::isEven, String::length, user::greet, ::User (a constructor).
Lambdas can capture and even modify local vars from the enclosing scope (unlike Java, which requires effectively final variables).
Anonymous functions (fun(x: Int): Int { return x * 2 }) allow normal return semantics when you need them.
Kotlin's standard library is built on these: map, filter, sortedBy, let, apply, use, lazy, thread { }.
Example
fun applyTwice(f: (Int) -> Int, x: Int): Int = f(f(x))
fun isEven(n: Int) = n % 2 == 0
fun makeCounter(): () -> Int {
var count = 0
return { ++count } // captures and mutates count
}
fun main() {
val add: (Int, Int) -> Int = { a, b -> a + b }
println(add(2, 3)) // 5
println(applyTwice({ it * 3 }, 2)) // 18
val nums = listOf(1, 2, 3, 4)
println(nums.filter(::isEven)) // [2, 4]
println(listOf("a", "bcd").map(String::length)) // [1, 3]
repeat(2) { i -> print("r$i ") } // r0 r1
println()
val next = makeCounter()
next(); next()
println(next()) // 3
nums.forEach {
if (it == 2) return@forEach // skip, like continue
print(it) // 134
}
}Remember
- Function types: (A, B) -> R; lambdas: { a, b -> ... }
- it names a single parameter; trailing lambdas go outside parentheses
- ::name creates function references
- Use return@label to return from a lambda
Watch out for
- Writing return inside a forEach lambda and unintentionally returning from the whole function.