Functions & Lambdas

Kotlin Fundamentals — Functions & Lambdas
Kotlin Fundamentals
Course 1 · Chapter 2 · Functions & Lambdas

🔧 Functions & Lambdas

Functions are where Kotlin's "less boilerplate than Java, more structure than JavaScript" philosophy shows up clearly. This chapter covers declaring functions, giving parameters default values, calling with named arguments, writing single-expression functions, and Kotlin's lambda syntax — the foundation for the functional-style APIs (like collection operators) used constantly later in this course.

📐 Declaring a Function

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

Every parameter must have an explicit type — Kotlin does not infer parameter types the way it infers val types, since the compiler has no value to look at until the function is called. The return type (: String above) can often be omitted and inferred, but many teams write it explicitly on public functions for readability.

Function Syntax: Java vs JavaScript vs Kotlin

LanguageDeclaring greet(name)
Javapublic String greet(String name) { return "Hello, " + name; }
JavaScriptfunction greet(name) { return `Hello, ${name}!`; }
Kotlinfun greet(name: String): String { return "Hello, $name!" }

➡️ Single-Expression Functions

When a function body is just a single return expression, Kotlin lets you drop the braces and the return keyword entirely, using = instead:

// Long form fun square(x: Int): Int { return x * x } // Single-expression form — equivalent fun square(x: Int) = x * x

The return type is inferred from the expression (Int * Int is Int), so it's usually left off in single-expression form. This isn't just shorthand for its own sake — Kotlin code leans heavily on small, expression-bodied functions, especially once lambdas and collection operators enter the picture later in the course.

Default Parameters

A parameter can have a default value, making the argument optional at the call site: fun greet(name: String, greeting: String = "Hello"). Calling greet("Philip") uses the default; greet("Philip", "Hi") overrides it.

Named Arguments

Any call can name its arguments explicitly: greet(name = "Philip", greeting = "Hi"). This lets you skip earlier defaults and go straight to a later one, or just make a call with many parameters self-documenting.

fun createUser(name: String, age: Int = 18, isAdmin: Boolean = false) { println("$name, age $age, admin=$isAdmin") } createUser("Alice") // Alice, age 18, admin=false createUser("Bob", 25) // Bob, age 25, admin=false createUser("Carol", isAdmin = true) // Carol, age 18, admin=true — skips age via named arg
⚠ No Function Overloading Juggling Needed

In Java, "optional" parameters are usually simulated with several overloaded method signatures (createUser(name), createUser(name, age), createUser(name, age, isAdmin)) — three methods to maintain in sync. Kotlin's default parameters replace all of that with one function.

🎯 Lambda Basics

A lambda is a function without a name, written inline and often passed as an argument — conceptually identical to a JavaScript arrow function:

// Kotlin lambda, stored in a val val square = { x: Int -> x * x } println(square(5)) // 25 // Passed directly as an argument, e.g. to a collection operator val numbers = listOf(1, 2, 3, 4) val doubled = numbers.map { n -> n * 2 } println(doubled) // [2, 4, 6, 8] // Single parameter can use the implicit "it" val tripled = numbers.map { it * 3 } println(tripled) // [3, 6, 9, 12]

Lambda Syntax: JavaScript vs Kotlin

JavaScriptKotlin
Arrow / lambda(x) => x * x{ x: Int -> x * x }
Separator=>-> (inside braces)
Implicit single paramNot availableit
Trailing lambda callarr.map((x) => x * 2)list.map { it * 2 }

That last row matters: when a lambda is the last argument to a function — which it is for almost every collection operator (map, filter, forEach...) — Kotlin lets you move it outside the parentheses entirely, and drop the (now-empty) parentheses altogether. numbers.map({ it * 2 }) and numbers.map { it * 2 } are the same call; the second is idiomatic Kotlin.

💻 Coding Challenges

Challenge 1: Default Parameters & Named Arguments

Write a function orderPizza(size: String = "medium", toppings: Int = 1, extraCheese: Boolean = false) that prints an order summary. Call it four times: with no arguments, with only size overridden, with only extraCheese overridden using a named argument, and with all three provided.

Goal: Get comfortable mixing default values with named-argument overrides.

→ Solution

Challenge 2: Single-Expression Functions

Write three functions as single-expression functions (using =, no braces or return): isEven(n: Int) returning a Boolean, max(a: Int, b: Int) returning the larger value, and greeting(name: String) returning a greeting string using a string template.

Goal: Practice recognizing when a function body is simple enough to collapse to a single expression.

→ Solution

Challenge 3: Lambdas with map and filter

Given val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), use a lambda with filter to keep only even numbers, then use a lambda with map to square each of the results. Print the final list. Write the lambdas using the implicit it parameter.

Goal: Chain two lambda-based collection operators and get comfortable with it.

→ Solution

💡 Trailing Lambda Syntax Is Everywhere

The "lambda moved outside the parentheses" pattern (list.map { it * 2 } instead of list.map({ it -> it * 2 })) shows up constantly in idiomatic Kotlin — not just for collections, but for things like building UI layouts in Jetpack Compose later on. Getting used to reading it now pays off throughout the rest of this course.

🎯 What's Next

Next chapter: Null Safety — nullable types, the safe call operator ?., the Elvis operator ?:, smart casts, and the not-null assertion !!.