Step 7 of 13
Lambdas, higher-order functions, function types, scope functions, inline functions, tail recursion
Lambdas, higher-order functions, scope functions (apply, let, also), inline, และ tailrec
A lambda is a function without a name. You can store it in a variable and pass it around:
// Lambda stored in a variable
val greet: (String) -> String = { name -> "Hello, $name!" }
// Call it
println(greet("Alice")) // Hello, Alice!
// Type inference works too
val square = { x: Int -> x * x }
println(square(5)) // 25
A higher-order function takes a function as a parameter or returns one:
// Takes a function as parameter
fun applyTwice(x: Int, f: (Int) -> Int): Int {
return f(f(x))
}
val result = applyTwice(3) { it * 2 }
println(result) // 12 (3 * 2 = 6, 6 * 2 = 12)
it KeywordWhen a lambda has exactly one parameter, Kotlin names it it automatically:
val numbers = listOf(1, 2, 3, 4, 5)
// Explicit parameter name
val doubled1 = numbers.map { n -> n * 2 }
// Same thing with `it`
val doubled2 = numbers.map { it * 2 }
println(doubled1) // [2, 4, 6, 8, 10]
println(doubled2) // [2, 4, 6, 8, 10]
Kotlin has explicit function types:
// (Int, Int) -> Int: takes two Ints, returns Int
val add: (Int, Int) -> Int = { a, b -> a + b }
// () -> Unit: takes nothing, returns nothing
val printHello: () -> Unit = { println("Hello") }
// (String) -> Boolean: takes a String, returns Boolean
val isLong: (String) -> Boolean = { it.length > 5 }
println(add(3, 4)) // 7
printHello() // Hello
println(isLong("Hello!")) // true
Pass an existing function using :::
fun isEven(n: Int): Boolean = n % 2 == 0
val numbers = listOf(1, 2, 3, 4, 5, 6)
// Pass the function directly
val evens = numbers.filter(::isEven)
println(evens) // [2, 4, 6]
Kotlin has five scope functions for working with objects. They reduce boilerplate:
| Function | Receiver | Returns | Use case |
|---|---|---|---|
let | it | lambda result | Null checks, transformations |
run | this | lambda result | Compute something from object |
with | this | lambda result | Group calls on same object |
apply | this | the object | Configure object |
also | it | the object | Side effects (logging, debugging) |
data class ServerConfig(
var host: String = "localhost",
var port: Int = 8080,
var debug: Boolean = false
)
val config = ServerConfig().apply {
host = "production.example.com"
port = 443
debug = false
}
println(config)
// ServerConfig(host=production.example.com, port=443, debug=false)
val email: String? = "alice@example.com"
val domain = email?.let {
it.substringAfter("@")
}
println(domain) // example.com
val names = listOf("Alice", "Bob", "Charlie")
.also { println("Before filter: $it") }
.filter { it.length > 3 }
.also { println("After filter: $it") }
.map { it.uppercase() }
println(names)
// Before filter: [Alice, Bob, Charlie]
// After filter: [Alice, Charlie]
// [ALICE, CHARLIE]
Kotlin can inline the body of a function at the call site. This removes the overhead of creating lambda objects:
inline fun measureTime(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}
val elapsed = measureTime {
repeat(1_000_000) { /* some work */ }
}
println("Took ${elapsed}ms")
The inline keyword is useful for higher-order functions that are called frequently.
Use tailrec to write recursive functions without stack overflow. The compiler converts them to loops:
tailrec fun factorial(n: Long, acc: Long = 1): Long {
return if (n <= 1) acc else factorial(n - 1, acc * n)
}
println(factorial(5)) // 120
println(factorial(20)) // 2432902008176640000
Build a simple data processing pipeline:
data class Transaction(val type: String, val amount: Double)
fun main() {
val transactions = listOf(
Transaction("deposit", 1000.0),
Transaction("withdraw", 200.0),
Transaction("deposit", 500.0),
Transaction("withdraw", 100.0),
Transaction("fee", 10.0),
)
val balance = transactions
.filter { it.type in listOf("deposit", "withdraw") }
.fold(0.0) { acc, tx ->
when (tx.type) {
"deposit" -> acc + tx.amount
else -> acc - tx.amount
}
}
.also { println("Final balance: $$it") }
// Final balance: $1200.0
}