Kotlin Lambda Parameters
Lambda expressions in Kotlin are a powerful feature that allows you to write concise and functional code. Understanding how parameters work within lambdas is essential for effectively using this feature in your code.
Introduction
In Kotlin, lambda expressions are essentially anonymous functions that can be passed around as values. The parameters of these lambda expressions determine what data the lambda can work with when it's executed. This guide will explore how lambda parameters are defined, accessed, and used.
Basic Lambda Parameter Syntax
A lambda expression in Kotlin consists of:
- Parameter declarations (optional)
- An arrow symbol (
->
) - The body of the function
The general syntax looks like this:
val lambdaName: (ParameterTypes) -> ReturnType = { parameters -> body }
Let's look at a simple example:
val square: (Int) -> Int = { number -> number * number }
// Using the lambda
println(square(5)) // Output: 25
println(square(7)) // Output: 49
In this example, number
is the parameter of our lambda, which represents the input value we want to square.
Implicit Parameter: it
When a lambda has only one parameter, Kotlin allows you to omit the parameter declaration and arrow symbol, and use the implicit name it
instead:
val square: (Int) -> Int = { it * it }
// Using the lambda
println(square(5)) // Output: 25
println(square(7)) // Output: 49
The it
identifier makes your code more concise for simple, single-parameter lambdas.
Multiple Parameters
Lambdas can have multiple parameters:
val sum: (Int, Int) -> Int = { a, b -> a + b }
// Using the lambda
println(sum(3, 4)) // Output: 7
println(sum(10, 20)) // Output: 30
With multiple parameters, you must explicitly name all parameters before the arrow.
Parameter Types
You can either declare parameter types explicitly or let Kotlin infer them from the context:
Explicit Parameter Types
val calculate: (Int, Int, String) -> Int = { a: Int, b: Int, operation: String ->
when (operation) {
"add" -> a + b
"subtract" -> a - b
"multiply" -> a * b
else -> a / b
}
}
println(calculate(10, 5, "add")) // Output: 15
println(calculate(10, 5, "subtract")) // Output: 5
Inferred Parameter Types
val calculate = { a: Int, b: Int, operation: String ->
when (operation) {
"add" -> a + b
"subtract" -> a - b
"multiply" -> a * b
else -> a / b
}
}
println(calculate(10, 5, "multiply")) // Output: 50
Default Parameters in Lambdas
Unlike regular functions, lambdas don't support default parameter values directly. However, you can achieve similar behavior using function references or wrapper functions:
// Function with default parameters
fun operation(a: Int, b: Int, isMultiply: Boolean = false): Int {
return if (isMultiply) a * b else a + b
}
// Creating lambdas from the function
val multiply = { a: Int, b: Int -> operation(a, b, true) }
val add = { a: Int, b: Int -> operation(a, b) }
println(multiply(4, 5)) // Output: 20
println(add(4, 5)) // Output: 9
Lambda Parameter Destructuring
Kotlin allows destructuring of objects in lambda parameters, which is particularly useful when working with pairs, maps, or custom objects:
val userData = mapOf(
"Alice" to 29,
"Bob" to 31,
"Charlie" to 25
)
// Using destructuring in lambda parameters
userData.forEach { (name, age) ->
println("$name is $age years old")
}
// Output:
// Alice is 29 years old
// Bob is 31 years old
// Charlie is 25 years old
Without destructuring, you would access the entries like this:
userData.forEach { entry ->
println("${entry.key} is ${entry.value} years old")
}
Real-world Applications
Example 1: Filtering a Collection
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Using a lambda to filter even numbers
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers) // Output: [2, 4, 6, 8, 10]
// Using a lambda with a named parameter for clarity
val divisibleByThree = numbers.filter { num -> num % 3 == 0 }
println(divisibleByThree) // Output: [3, 6, 9]
Example 2: Creating a Simple Event Listener
// Defining a button click handler type
typealias OnClickListener = (Int) -> Unit
class Button(val id: Int) {
private var clickListener: OnClickListener? = null
fun setOnClickListener(listener: OnClickListener) {
clickListener = listener
}
fun click() {
clickListener?.invoke(id)
}
}
// Using the button with a lambda
val myButton = Button(42)
myButton.setOnClickListener { buttonId ->
println("Button $buttonId was clicked!")
}
myButton.click() // Output: Button 42 was clicked!
Example 3: Custom Sorter with Multiple Parameters
data class Person(val name: String, val age: Int)
val people = listOf(
Person("Alice", 29),
Person("Bob", 31),
Person("Charlie", 25)
)
// Custom sorter with two parameters
val customSorter: (Person, Person) -> Int = { p1, p2 ->
when {
p1.age != p2.age -> p1.age - p2.age
else -> p1.name.compareTo(p2.name)
}
}
val sortedPeople = people.sortedWith(Comparator { p1, p2 -> customSorter(p1, p2) })
println(sortedPeople)
// Output: [Person(name=Charlie, age=25), Person(name=Alice, age=29), Person(name=Bob, age=31)]
Type Inference with Lambda Parameters
Kotlin's type inference system can often determine parameter types from context, making your code cleaner:
val numbers = listOf(1, 2, 3, 4, 5)
// The type of 'number' is inferred to be Int
numbers.forEach { number ->
println("Square of $number is ${number * number}")
}
// Output:
// Square of 1 is 1
// Square of 2 is 4
// Square of 3 is 9
// Square of 4 is 16
// Square of 5 is 25
Lambda Parameters vs. Function Parameters
It's important to understand the distinction between the parameters of a lambda and the parameters when declaring a variable that will hold a lambda:
// This is a variable that can hold a function taking an Int and returning a String
val formatter: (Int) -> String = { number ->
"The number is $number"
}
// Using our lambda
println(formatter(42)) // Output: The number is 42
In this example:
(Int) -> String
defines what kind of lambdas can be assigned toformatter
number
is the parameter name used inside the lambda
Summary
Lambda parameters in Kotlin provide a flexible way to pass input data to anonymous functions. Key points to remember:
- For a single parameter, you can use the implicit
it
identifier - For multiple parameters, list them before the arrow (
->
) - Parameter types can be explicitly declared or inferred
- Destructuring is available for working with composite parameters
- Lambdas don't support default parameters directly, but you can work around this
Being comfortable with lambda parameters will significantly enhance your ability to write concise, functional Kotlin code and take full advantage of Kotlin's collection processing capabilities.
Exercises
- Write a lambda that takes two integers and returns the larger one
- Create a function that accepts a lambda with three parameters (two strings and one integer) and calls it
- Use a lambda with destructuring to print a list of pairs in the format "key = value"
- Write a higher-order function that applies a mathematical operation (passed as a lambda) to two numbers
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)