Functions
Learn Kotlin function syntax, default and named parameters, single-expression functions, varargs, extension functions, and lambdas.
Introduction
Functions are Kotlin's core unit of reusable logic, declared with the `fun` keyword. Kotlin functions come with several conveniences Java lacks outright — default parameter values, named arguments, single-expression bodies, and the ability to attach new functions to existing types without modifying them.
This lesson covers everything from a basic function declaration through extension functions and lambdas, the building blocks behind Kotlin's expressive standard library.
Declaring a Function
A function is declared with `fun`, a name, a parameter list with explicit types, and an optional return type. If a function returns nothing meaningful, its return type is `Unit` (roughly equivalent to `void`), which can be omitted.
fun add(a: Int, b: Int): Int { return a + b}
fun greet(name: String) { // returns Unit, no explicit return type needed println("Hello, $name!")}
fun main() { println(add(3, 4)) greet("Kotlin")}Click Run to see what this code prints.
Default Parameters
Kotlin lets you give a parameter a default value, so callers can omit it entirely. This removes the need for the method-overloading pattern Java relies on to simulate optional arguments.
fun greet(name: String, greeting: String = "Hello") { println("$greeting, $name!")}
fun main() { greet("Aisha") // uses the default greeting greet("Marco", "Ciao") // overrides the default}Click Run to see what this code prints.
Named Arguments
Arguments can be passed by name instead of position, which is especially useful for functions with several parameters, or several of the same type where argument order is easy to mix up.
fun createUser(name: String, isAdmin: Boolean = false, age: Int = 18) { println("$name, admin=$isAdmin, age=$age")}
fun main() { createUser(name = "Sam", age = 25) // skips isAdmin, uses its default createUser("Devi", isAdmin = true) // mixes positional and named}Click Run to see what this code prints.
Single-Expression Functions
When a function body is a single expression, you can skip the curly braces and `return` entirely, writing it after an `=` sign instead. The return type can usually be inferred, so it is commonly omitted too.
fun square(x: Int) = x * xfun isEven(n: Int) = n % 2 == 0
fun main() { println(square(6)) println(isEven(7))}Click Run to see what this code prints.
Varargs
The `vararg` modifier lets a function accept a variable number of arguments of the same type, which are then available inside the function as an array.
fun sumAll(vararg numbers: Int): Int { var total = 0 for (n in numbers) total += n return total}
fun main() { println(sumAll(1, 2, 3)) println(sumAll(10, 20, 30, 40, 50))}Click Run to see what this code prints.
Extension Functions
One of Kotlin's most distinctive features is the extension function — it lets you add a new function to an existing type, including types you don't own (like `String` or a class from a Java library), without modifying its source or using inheritance.
fun String.shout(): String { return this.uppercase() + "!"}
fun main() { val message = "kotlin is fun" println(message.shout()) // called just like a built-in method}Click Run to see what this code prints.
Extension functions are resolved at compile time based on the declared type, not real inheritance — under the hood, the compiler turns `message.shout()` into an ordinary static-style function call. They're purely a readability and organization feature.
Lambdas & Higher-Order Functions
A lambda is a small, anonymous function you can pass around as a value. A higher-order function is any function that takes a lambda as a parameter or returns one — this pattern is used everywhere in Kotlin's standard library, especially with collections.
fun applyOperation(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b)}
fun main() { val sum = applyOperation(4, 5) { x, y -> x + y } val product = applyOperation(4, 5) { x, y -> x * y }
println(sum) println(product)}Click Run to see what this code prints.
The `{ x, y -> x + y }` syntax is a lambda: its parameters come before the `->`, and the expression after it is the value it returns. When a lambda is the last parameter of a function, Kotlin lets you move it outside the parentheses, which is why `applyOperation(4, 5) { x, y -> x + y }` reads almost like built-in syntax rather than a plain function call.
Common Mistakes
- Writing method overloads to simulate optional parameters — use default parameter values instead.
- Forgetting that default parameters can be skipped only with named arguments when they aren't the last parameters in the list.
- Overusing extension functions for logic that has nothing to do with the type being extended — they should feel like a natural addition to that type.
- Confusing a lambda's trailing-lambda syntax (`list.filter { it > 0 }`) with a separate special construct — it's just a regular function call with the last argument moved outside the parentheses.
Best Practices
- Use single-expression syntax for short, clear functions — it removes visual noise for simple logic.
- Prefer named arguments when calling functions with several parameters of the same type, or several boolean flags.
- Keep extension functions focused and discoverable — put them near the type they extend or in a clearly named file.
- Use `it` (the implicit single-parameter name) in short lambdas, but switch to an explicit parameter name once the lambda body gets more than a line or two.
Frequently Asked Questions
No. For single-expression functions and most local values, Kotlin infers the return type automatically. Explicit return types are still good practice for public API functions, for documentation clarity.
Unit is Kotlin's equivalent of Java's void, but it is an actual type with a single instance, which makes it usable in generic code (like a lambda type) in ways void cannot be.
No. Extension functions only have access to the public (and internal/protected, depending on visibility and location) members of the type — they cannot break real encapsulation.
Key Takeaways
- Functions are declared with `fun`; a `Unit` return type (roughly "void") can be omitted.
- Default parameter values and named arguments remove most of the need for method overloading.
- Single-expression functions (`fun square(x: Int) = x * x`) cut boilerplate for simple logic.
- Extension functions add new behavior to existing types, including types you don't own, without inheritance.
- Lambdas and higher-order functions are core to idiomatic Kotlin, especially when working with collections.
Summary
You now know how to write flexible, expressive Kotlin functions — from simple one-liners to lambdas and extension functions. Next, you'll learn how to model real-world data using classes and objects.