Skip to main content

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:

kotlin
val lambdaName: (ParameterTypes) -> ReturnType = { parameters -> body }

Let's look at a simple example:

kotlin
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:

kotlin
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:

kotlin
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

kotlin
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

kotlin
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:

kotlin
// 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:

kotlin
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:

kotlin
userData.forEach { entry ->
println("${entry.key} is ${entry.value} years old")
}

Real-world Applications

Example 1: Filtering a Collection

kotlin
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

kotlin
// 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

kotlin
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:

kotlin
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:

kotlin
// 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 to formatter
  • 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

  1. Write a lambda that takes two integers and returns the larger one
  2. Create a function that accepts a lambda with three parameters (two strings and one integer) and calls it
  3. Use a lambda with destructuring to print a list of pairs in the format "key = value"
  4. 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! :)