Skip to content
beginner Phase 1 · Kotlin Foundations

Functions & Lambdas

Define functions, default parameters, named arguments, single-expression functions, and lambdas.

45m
3 problems
Topic Progress 0%

Function Declarations

Why Functions Matter

Functions are the primary unit of code organization. A well-designed function does one thing, has a clear name, and can be tested in isolation. Kotlin's function syntax reduces boilerplate compared to Java while maintaining full expressiveness.

Basic Syntax

fun greet(name: String): String {
    return "Hello, $name!"
}

// Usage
println(greet("Amazon"))  // Hello, Amazon!

The return type goes after the parameter list. If a function returns nothing, the return type is omitted (Unit is implied):

fun printGreeting(name: String) {
    println("Hello, $name!")
    // Implicitly returns Unit
}

Single-Expression Functions

When a function body is a single expression, you can omit the braces and use =:

fun add(a: Int, b: Int): Int = a + b

// Return type is inferred
fun multiply(a: Int, b: Int) = a * b

Single-expression functions are not just shorter. They signal to the reader that the function is a simple transformation with no side effects.

Default Parameters

Kotlin eliminates method overloading for default values. You declare defaults directly in the signature:

fun log(message: String, level: String = "INFO") {
    println("[$level] $message")
}

log("Server started")           // [INFO] Server started
log("Disk full", "WARN")       // [WARN] Disk full

In Java, you would need two overloaded methods. In Kotlin, one function handles both cases.

Named Arguments

When calling a function, you can name the arguments to improve clarity and skip defaults:

fun createUser(
    name: String,
    age: Int,
    email: String = "",
    active: Boolean = true
) {
    println("$name, age $age, active=$active")
}

// Skip default parameters by name
createUser(name = "Alice", age = 30)

// Reorder named arguments
createUser(active = false, name = "Bob", age = 25)

Named arguments are especially valuable when a function has many parameters of the same type. Without names, it is easy to swap two String or Int arguments by mistake.

Explicit Return Types

Kotlin can infer return types for non-recursive functions, but public API functions should declare them explicitly for documentation:

// Inferred return type (fine for private/internal)
fun doubleIt(x: Int) = x * 2

// Explicit return type (better for public API)
fun doubleIt(x: Int): Int = x * 2

Lambdas and Higher-Order Functions

First-Class Functions

In Kotlin, functions are first-class citizens. You can store them in variables, pass them as arguments, and return them from other functions. This is the foundation of functional programming patterns.

Lambda Expressions

A lambda is an anonymous function defined inline:

val double: (Int) -> Int = { x -> x * 2 }
println(double(5))  // 10

The syntax is { parameters -> body }. When a lambda has a single parameter, you can use it instead of naming it:

val isEven: (Int) -> Boolean = { it % 2 == 0 }
println(isEven(4))  // true

Passing Lambdas to Functions

Functions that accept lambdas as parameters are called higher-order functions. The standard library is full of them:

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

// filter, map, reduce all take lambdas
val evens = numbers.filter { it % 2 == 0 }       // [2, 4]
val doubled = numbers.map { it * 2 }               // [2, 4, 6, 8, 10]
val sum = numbers.reduce { acc, i -> acc + i }     // 15

Trailing Lambda Syntax

When the last parameter of a function is a lambda, you can move it outside the parentheses. When it is the only parameter, you can drop the parentheses entirely:

// These are all equivalent
numbers.filter({ it > 2 })
numbers.filter() { it > 2 }
numbers.filter { it > 2 }   // Preferred

This trailing lambda syntax makes higher-order functions read like built-in language constructs.

Function Types

Every lambda has a function type. You can use them as parameter types and return types:

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

val sum = applyOperation(3, 4) { a, b -> a + b }       // 7
val product = applyOperation(3, 4) { a, b -> a * b }    // 12

Returning Values from Lambdas

The last expression in a lambda is the return value. Use return@label to exit from an enclosing function:

fun processNumbers(numbers: List<Int>) {
    numbers.forEach {
        if (it < 0) return@forEach  // Skips negative, continues loop
        println(it)
    }
    println("Done")
}

Lambdas vs Anonymous Classes

Kotlin lambdas compile to the same bytecode as Java anonymous classes when implementing single-abstract-method interfaces. This means lambdas work seamlessly with Java APIs:

// Kotlin lambda
view.setOnClickListener { println("Clicked") }

// Equivalent Java anonymous class
// view.setOnClickListener(new OnClickListener() { ... })

Practice Problems

0 / 3 solved
String Transformer

Write a function transform that takes a String and a lambda (String) -> String. Apply the lambda to the string and return the result. Then call it with lambdas that uppercase the string and reverse it.

Solution
fun transform(input: String, operation: (String) -> String): String {
    return operation(input)
}

fun main() {
    val result1 = transform("hello") { it.uppercase() }
    println(result1)  // HELLO

    val result2 = transform("hello") { it.reversed() }
    println(result2)  // olleh
}
Default and Named Arguments

Write a function createProfile that accepts name (String), age (Int, default 0), bio (String, default empty), and isPublic (Boolean, default true). Use named arguments to call it with only the required parameters, then with custom values.

Solution
fun createProfile(
    name: String,
    age: Int = 0,
    bio: String = "",
    isPublic: Boolean = true
) {
    println("$name, age=$age, bio=$bio, public=$isPublic")
}

fun main() {
    createProfile(name = "Alice")
    // Alice, age=0, bio=, public=true

    createProfile(name = "Bob", age = 30, bio = "Engineer", isPublic = false)
    // Bob, age=30, bio=Engineer, public=false
}
Lambda Chain Processor

Write a function processList that takes a List<Int> and a list of transform lambdas (List<(Int) -> Int>). Apply each transform in sequence to every element and return the final list.

Solution
fun processList(
    numbers: List<Int>,
    transforms: List<(Int) -> Int>
): List<Int> {
    return transforms.fold(numbers) { current, transform ->
        current.map(transform)
    }
}

fun main() {
    val result = processList(
        listOf(1, 2, 3, 4),
        listOf(
            { it * 2 },        // [2, 4, 6, 8]
            { it + 10 },       // [12, 14, 16, 18]
            { it * it }         // [144, 196, 256, 324]
        )
    )
    println(result)  // [144, 196, 256, 324]
}

Quiz

1. What is the advantage of default parameters in Kotlin over Java method overloading?

Question 1 options

2. What does the trailing lambda syntax allow?

Question 2 options

3. What does the following code print? ```kotlin val f: (Int, Int) -> Int = { a, b -> a * b } println(f(3, 4)) ```

Question 3 options

4. When should you use an explicit return type on a Kotlin function?

Question 4 options

Flashcards

Question

What is a higher-order function in Kotlin?

Answer

A function that takes one or more functions as parameters, or returns a function. Examples: map, filter, sortedBy, run, let.

Question

What is the trailing lambda syntax?

Answer

When the last parameter of a function is a lambda, you can move it outside the parentheses. If it is the only parameter, you can drop the parentheses entirely. Example: list.filter { it > 5 }.

Question

What does the 'it' keyword refer to in a Kotlin lambda?

Answer

When a lambda has a single parameter, Kotlin implicitly names it 'it'. You can reference it without declaring it. Use the named form if there are multiple parameters or for clarity.

Question

How do you write a single-expression function in Kotlin?

Answer

Use the = operator instead of braces and an explicit return. Example: fun double(x: Int) = x * 2. The return type is inferred.

Revision Notes

Key Takeaways

  • 1. Use single-expression functions when the body is one expression.
  • 2. Default parameters replace method overloading in Kotlin.
  • 3. Named arguments prevent parameter order mistakes and improve readability.
  • 4. Lambdas are first-class: pass them as arguments, store in variables, return from functions.
  • 5. Trailing lambda syntax makes higher-order function calls read naturally.

Interview Tips

  • Explain the difference between a function reference (::functionName) and a lambda.
  • Know that Kotlin lambdas compile to anonymous classes for Java SAM interfaces.
  • Be ready to explain when default parameters are better than overloading.
  • Practice writing and passing lambdas to standard library functions like map, filter, and reduce.

Cheat Sheet

Functions & Lambdas Cheat Sheet

Function Declaration:

  • fun name(param: Type): ReturnType { body }
  • Single-expression: fun name(param: Type) = expression

Default Parameters:

  • fun log(msg: String, level: String = "INFO")
  • Eliminates Java-style overloading

Named Arguments:

  • Call with func(name = "Alice", age = 30)
  • Can skip defaults, reorder arguments

Lambdas:

  • { x -> x * 2 } — basic syntax
  • { it * 2 } — single parameter shorthand
  • Function type: (Int) -> Int

Higher-Order Functions:

  • Functions that accept or return lambdas
  • Trailing lambda: list.filter { it > 5 }