Kotlin lambda expression tutorial shows how to use lambda expressions in Kotlin. A lambda expression is an anonymous function which is treated as a value.
last modified January 29, 2024
In this article we show how to use lambda expressions in Kotlin.
A lambda expression is an anonymous function which is treated as a value. It can be bound to a variable, passed to a function as a parameter, or returned from a function.
val square: (Int) -> Int = { e: Int -> e * e }
In Kotlin, a lambda expression is always delimited by curly braces.
Anonymous functions are functions that do not have a name.
Main.kt
package com.zetcode
fun main() {
val vals = intArrayOf(-2, -1, 0, 1, 2, 3, 4)
val filtered = vals.filter(fun(e) = e > 0)
println(filtered)
}
We define an array of integers. The array is filtered with the filter function. The filter function takes an anonymous function as a parameter.
val filtered = vals.filter(fun(e) = e > 0)
The anonymous function is used to filter the array.
The next example is a rewrite of the previous one using a lambda expression.
Main.kt
package com.zetcode
fun main() {
val vals = intArrayOf(-2, -1, 0, 1, 2, 3, 4)
val filtered = vals.filter { e -> e > 0 }
println(filtered)
}
Here we use curly braces and the -> operator.
In a lambda expression type declaration, we have a list of parameters in square brackets followed by the arrow -> operator followed by the return type.
Main.kt
package com.zetcode
fun main() {
val square: (Int) -> Int = { e: Int -> e * e }
val r1 = square(5)
val r2 = square(3)
println(r1)
println(r2)
}
In the program, we bind a lambda expression to a value; full type declarations are provided.
val square: (Int) -> Int = { e: Int -> e * e }
The square value is bound to a lambda expression, which accepts one integer and returns an integer.
Kotlin can infer the data types of values and therefore, we can omit some declarations.
Main.kt
package com.zetcode
fun main() {
val square1: (Int) -> Int = { e: Int -> e * e }
val square2 = { e: Int -> e * e }
val square3: (Int) -> Int = { e -> e * e }
// val square4 = { e -> e * e }
val r1 = square1(5)
val r2 = square2(3)
val r3 = square3(6)
println(r1)
println(r2)
println(r3)
}
In the example, we define the square function multiple times.
val square1: (Int) -> Int = { e: Int -> e * e }
This is the full type declaration.
val square2 = { e: Int -> e * e }
Here we omit the lambda declaration for the square2 name.
val square3: (Int) -> Int = { e -> e * e }
In this case, we omit the declaration of the element inside the lambda expression.
// val square4 = { e -> e * e }
However, we cannot omit both declarations. This code does not compile.
The Unit type is used for an expression that does not return a value.
Main.kt
package com.zetcode
fun main() {
val l1 = { println("Hello there!") }
val l2: (String) -> Unit = { name: String ->
println("Hello $name!")
}
l1()
l2("Lucia")
}
If we print something to the console, we do not return anything. For such cases, we specify Unit.
The it is a special keyword that represents a single parameter passed to the lambda expression.
Main.kt
package com.zetcode
fun main() {
val nums = listOf(1, 2, 3, 4, 5, 6)
nums.forEach { println(it * 2) }
}
We have a list of integers. With forEach, we go through the list of elements and multiply them by two.
nums.forEach { println(it * 2) }
The it represents the currently processed item.
In the following example, we pass lambda expressions as function arguments.
Main.kt
package com.zetcode
val inc = { e: Int -> e + 1 } val dec = { e: Int -> e - 1 } val square = { e: Int -> e * e } val triple = { e: Int -> e * e * e }
fun doProcess(vals: List<Int>, f: (Int) -> Int) {
val processed = vals.map { e -> f(e) }
println(processed)
}
fun main() {
val vals = listOf(1, 2, 3, 4, 5, 6)
doProcess(vals, inc)
doProcess(vals, dec)
doProcess(vals, square)
doProcess(vals, triple)
}
We define four lambdas: inc, dec, square, triple. We pass the lambdas to the doProcess function.
fun doProcess(vals: List<Int>, f: (Int) -> Int) {
val processed = vals.map { e -> f(e) }
println(processed)
}
We provide the type declaration for the second parameter: (Int) -> Int. Each lambda takes an integer and returns an integer.
val processed = vals.map { e -> f(e) }
With map, we apply the lambda expression on each element of the list.
The last expression in the lambda is returned.
Main.kt
package com.zetcode
val check = { u:Pair<String, Int> ->
when (u.second) {
in 0..75 -> "failed"
else -> "passed"
}
}
fun main() {
val students = listOf(
Pair("Maria", 98),
Pair("Pablo", 81),
Pair("Lucia", 45),
Pair("Peter", 98),
Pair("Simon", 73),
)
students.forEach {
val res = check(it)
println("${it.first} has $res")
}
}
In the example, we have a list of students. We check which students have passed the exam.
val check = { u:Pair<String, Int> ->
when (u.second) {
in 0..75 -> "failed"
else -> "passed"
}
}
The lambda contains a when expression. The matched arm’s value is returned from the lambda.
val res = check(it) println("${it.first} has $res")
The returned value is used to display a message.
If the last parameter of a function is a function, then a lambda expression can be placed outside the parentheses. If the lambda is the only argument, the parentheses can be omitted entirely.
Main.kt
package com.zetcode
data class User(val fname: String, val lname: String, val salary: Int)
fun main() {
val users = listOf(
User("John", "Doe", 1230),
User("Lucy", "Novak", 670),
User("Ben", "Walter", 2050),
User("Robin", "Brown", 2300),
User("Amy", "Doe", 1250),
User("Joe", "Draker", 1190),
User("Janet", "Doe", 980),
User("Albert", "Novak", 1930)
)
val r1 = users.maxBy({ u: User -> u.salary })
println(r1)
val r2 = users.maxBy() { u: User -> u.salary }
println(r2)
val r3 = users.maxBy { u: User -> u.salary }
println(r3)
}
In the example, we find the maximum salary of all users.
val r1 = users.maxBy({ u: User -> u.salary }) println(r1)
In the first case, we pass the lambda expression to the maxBy function as a parameter.
val r2 = users.maxBy() { u: User -> u.salary } println(r2)
Since the lambda is the last parameter, we can take it out of the parentheses.
val r3 = users.maxBy { u: User -> u.salary } println(r3)
Since the lambda is the only parameter, we can omit the parentheses.
We can chain funtion calls with lambda expressions, creating succinct code.
Main.kt
package com.zetcode
fun main() {
val words = listOf("sky", "cup", "water", "den",
"knife", "earth", "falcon")
val res = words.filter { it.length == 5 }.sortedBy { it }
.map { it.replaceFirstChar(Char::titlecase) }
println(res)
}
We filter a list of words, sort it, and capitalize its elements. All is done in a chain of three function calls. The functions have trailing lambdas.
Parameters can be destructured in lambda expressions. For unused variables, we can use the underscore _ character.
Main.kt
package com.zetcode
fun main() {
val words = mapOf(
1 to "sky", 2 to "cup", 3 to "water", 4 to "den",
5 to "knife", 6 to "earth", 7 to "falcon"
)
words.forEach { (_, v) -> println(v) }
}
We have a map of words. We go through the map with forEach. Each elemetn of the map is destructured into a key/value pair. Since we do not use the key, we use the underscore character.
Higher-order functions and lambdas
In this article we have covered Kotlin lambda expressions.
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Kotlin tutorials.