Functions in Kotlin
Learn how to define and use functions in Kotlin - parameters, default and named arguments, recursion, higher-order functions, lambdas, and extension functions with Docker-ready examples
Functions are the building blocks of any Kotlin program. They let you package logic into reusable, named units, give it clear inputs and outputs, and compose larger programs out of small, testable pieces. You have already met one function — main() — in the Hello World tutorial. Now we will look at how to write your own.
Kotlin is a multi-paradigm language, blending object-oriented and functional styles, and its function support reflects that. Functions can be declared at the top level (no class required), as members of a class (methods), or even as values you store in variables and pass around. They are first-class citizens: a function can take other functions as parameters and return functions as results.
This dual nature is one of Kotlin’s defining features. You can write straightforward procedural code when that’s clearest, then reach for higher-order functions, lambdas, and closures when a functional approach fits better. In this tutorial you will learn how to define functions, supply default and named arguments, write recursive functions, work with higher-order functions and lambdas, and extend existing types with extension functions.
Defining Functions
A Kotlin function is declared with the fun keyword, followed by a name, a parameter list, and an optional return type after a colon. When a function’s body is a single expression, you can drop the braces and the return keyword entirely.
Create a file named Functions.kt:
| |
Key things to notice:
- Parameter types are required — Kotlin is statically typed, so every parameter declares its type.
- Return types can be inferred for single-expression functions (
fun multiply(a: Int, b: Int) = a * balso works), but spelling them out improves readability. - Default arguments remove the need for the overload explosion common in Java.
- Named arguments make calls self-documenting and let you skip earlier defaults.
Unitis Kotlin’s equivalent ofvoid; it is the return type when a function produces no meaningful value and can be omitted.
Recursion and Scope
A function can call itself. Recursion is a natural way to express problems defined in terms of smaller subproblems, such as factorials. Variables declared inside a function are local to it — they exist only while the function runs and are not visible elsewhere.
Create a file named Recursion.kt:
| |
Here appName is a top-level value visible to every function in the file, while limit is local to main(). Kotlin uses if as an expression (it returns a value), so the recursive functions return the result of the if directly. For deep recursion, Kotlin also offers the tailrec modifier to optimize tail-recursive calls into loops, avoiding stack overflow.
Higher-Order Functions, Lambdas, and Closures
Because functions are first-class values, a function can accept another function as a parameter or return one. These are called higher-order functions. A lambda is a function written inline as an expression, and a closure is a function that captures variables from its surrounding scope.
Create a file named HigherOrder.kt:
| |
Notable Kotlin conventions:
- Trailing lambda syntax — when a function’s last parameter is a function, the lambda can go outside the parentheses:
calculate(5, 3) { x, y -> x + y }. it— a single-parameter lambda can refer to its argument asitinstead of naming it, as innumbers.map { it * 2 }.- Function types are written
(Int) -> Int, meaning “takes anInt, returns anInt.” - Closures capture variables (
factor) from the enclosing scope, even after the outer function has returned.
Methods and Extension Functions
In Kotlin’s object-oriented side, a function declared inside a class is called a method (or member function) and operates on instances of that class. Kotlin also supports extension functions, which let you add new methods to existing types — even ones you didn’t write — without inheritance.
Create a file named Methods.kt:
| |
Inside an extension function, this refers to the receiver — the value the function is called on. Extension functions are resolved statically (they don’t truly modify the class), but they read just like ordinary methods at the call site, which makes APIs feel natural and discoverable.
Running with Docker
Each example has its own main(), so compile and run them one at a time. The Zenika image bundles the Kotlin compiler (kotlinc) and a JVM.
| |
Expected Output
Running Functions.kt:
7
42
Hello, Kotlin!
Hi, Kotlin!
Welcome, Ada!
LOG: Functions are first-class
15
Running Recursion.kt:
Calculator starting
factorial(5) = 120
fib(5) = 5
Running HigherOrder.kt:
Sum: 8
Product: 15
Square of 6: 36
Triple 7: 21
Doubled: [2, 4, 6, 8, 10]
Evens: [2, 4]
Running Methods.kt:
20
Rectangle 4x5 with area 20
HELLO!
10 is even: true
7 is even: false
Key Concepts
fundeclares a function, with typed parameters and an optional return type; single-expression functions use=and need no braces orreturn.- Default and named arguments eliminate most overloading and make call sites self-documenting.
Unitis Kotlin’svoid-equivalent return type and can be omitted for functions that return nothing useful.- Functions are first-class values — they can be stored in variables, passed as arguments, and returned from other functions.
- Lambdas are inline function literals; the last lambda argument can sit outside the parentheses, and a single parameter is available as
it. - Closures capture variables from their surrounding scope and keep them alive after the enclosing function returns.
- Methods are functions that belong to a class, while extension functions add new methods to existing types without modifying or subclassing them.
- Recursion is fully supported, and the
tailrecmodifier optimizes tail-recursive functions to avoid stack overflow.
Comments
Loading comments...
Leave a Comment