Lambdas and Higher-Order Functions in Kotlin

1. Why Use Lambdas and Higher-Order Functions?

In Kotlin, functions are first-class citizens: they can be assigned to variables, passed as arguments, or returned from other functions. This capability introduces two powerful concepts:

These features improve code modularity, readability, and flexibility, especially when working with collections, callbacks, or functional programming patterns.


2. What is a Lambda Expression?

A lambda expression is an anonymous function without a name. It can be stored in a variable, passed directly as an argument to a function, or returned from a function. The general syntax:

{ parameters -> body }
Basic Lambda Examples

// Lambda stored in a variable
val greet: (String) -> Unit = { name -> println("Hello, $name!") }

// Lambda with inferred type
val sum = { a: Int, b: Int -> a + b }

// Lambda using 'it' for single parameter
val printMessage: (String) -> Unit = { println(it) }

fun main() {
    greet("Talha")              // Output: Hello, Talha!
    println(sum(5, 10))         // Output: 15
    printMessage("Kotlin Rocks!") // Output: Kotlin Rocks!
}
Passing Lambda as Parameter

// Higher-order function accepting a lambda
fun performOperation(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

fun main() {
    val sumResult = performOperation(10, 5) { x, y -> x + y }
    val productResult = performOperation(10, 5) { x, y -> x * y }

    println("Sum: $sumResult")       // Output: Sum: 15
    println("Product: $productResult") // Output: Product: 50
}
Returning Lambda from a Function

// Function returning a lambda
fun getOperation(type: String): (Int, Int) -> Int {
    return when(type) {
        "add" -> { a, b -> a + b }
        "multiply" -> { a, b -> a * b }
        else -> { _, _ -> 0 }
    }
}

fun main() {
    val operation = getOperation("add")
    println(operation(20, 30)) // Output: 50

    val multiplyOp = getOperation("multiply")
    println(multiplyOp(4, 5))  // Output: 20
}
Multiple Syntax Variations

// Lambda with explicit type
val divide: (Int, Int) -> Int = { a, b -> a / b }

// Anonymous function syntax
val divideAnon = fun(a: Int, b: Int): Int { return a / b }

fun main() {
    println(divide(10, 2))      // Output: 5
    println(divideAnon(10, 2))  // Output: 5
}

3. Type Inference in Lambdas

Kotlin can infer parameter and return types, so you don’t always need to explicitly declare them:


val sum = { a: Int, b: Int -> a + b }   // Type inferred as (Int, Int) -> Int
val square: (Int) -> Int = { it * it }  // Single parameter uses 'it'

fun main() {
    println(sum(5, 10))    // Output: 15
    println(square(4))     // Output: 16
}
    

Note: If a lambda has only one parameter, you can use the implicit it instead of naming it.


4. Higher-Order Functions

A higher-order function either:


// Function taking another function as parameter
fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

fun main() {
    val sum = operateOnNumbers(5, 3) { x, y -> x + y }
    val product = operateOnNumbers(5, 3) { x, y -> x * y }

    println("Sum: $sum")       // Output: Sum: 8
    println("Product: $product") // Output: Product: 15
}
    

5. Returning Functions

Kotlin allows functions to return other functions, enabling dynamic behavior:


// Function returning another function
fun getOperation(type: String): (Int, Int) -> Int {
    return when(type) {
        "add" -> { a, b -> a + b }
        "multiply" -> { a, b -> a * b }
        else -> { _, _ -> 0 }
    }
}

fun main() {
    val operation = getOperation("add")
    println(operation(10, 20)) // Output: 30
}
    

6. Practical Example — Lambdas with Collections

Lambdas are frequently used with Kotlin collections through functions like map, filter, and forEach.


fun main() {
    val numbers = listOf(1, 2, 3, 4, 5)

    val squared = numbers.map { it * it }
    val even = numbers.filter { it % 2 == 0 }

    println("Squared: $squared")
    println("Even Numbers: $even")
}
    

Output:


Squared: [1, 4, 9, 16, 25]
Even Numbers: [2, 4]
    

7. Difference Between Lambda and Anonymous Function

Both are unnamed, but with key differences:

Feature Lambda Expression Anonymous Function
Syntax { a, b -> a + b } fun(a: Int, b: Int): Int { return a + b }
Return Type Inferred automatically (last expression returned) Explicit return required
Return Behavior return exits enclosing function return exits only from the anonymous function itself
Use Case Concise, short-lived operations Complex logic, explicit control

// Lambda
val multiply = { a: Int, b: Int -> a * b }

// Anonymous function
val multiplyAnon = fun(a: Int, b: Int): Int {
    return a * b
}

fun main() {
    println(multiply(4, 5))     // 20
    println(multiplyAnon(4, 5)) // 20
}
    

8. When to Use and When Not to Use

✅ Use When:
🚫 Avoid When:

9. Common Interview Questions


10. Complete Example Program


fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

fun main() {
    val add = { x: Int, y: Int -> x + y }
    val subtract = { x: Int, y: Int -> x - y }
    val multiply = { x: Int, y: Int -> x * y }

    println("Addition: ${calculate(10, 5, add)}")
    println("Subtraction: ${calculate(10, 5, subtract)}")
    println("Multiplication: ${calculate(10, 5, multiply)}")
}
    

Output:


Addition: 15
Subtraction: 5
Multiplication: 50
    

Summary:
Lambdas and higher-order functions enable concise, flexible, and expressive code in Kotlin. They simplify operations, improve readability, support functional programming patterns, and are an essential skill for modern Kotlin development.