As programs grow, you find yourself writing the same steps again and again: calculating a price with tax, formatting a date, checking a password. A function packages those steps under a name, so you write them once and call them whenever you need them.
Kotlin functions are declared with fun, and you've already written one: main. Functions can take parameters (inputs) and return a result. Kotlin adds some genuinely useful touches: parameters can have default values, callers can name the arguments they pass, and a function whose body is a single expression can be written on one line.
In this lesson you'll write functions with parameters and return values, shorten them to single expressions, use default and named arguments to make calls clearer, accept any number of arguments with vararg, and return two values at once.
Declaring and calling functions
fun greet(name: String) {
println("Hello, $name!")
}
fun area(width: Double, height: Double): Double {
return width * height
}
greet("Ada")
val room = area(4.0, 3.5)- Each parameter has a name and a type:
name: String. - The return type comes after the brackets:
: Double.returnsends the result back. - A function that returns nothing useful has the type
Unit, which you can leave out, as ingreet. - Parameters are read-only, like
val.
Single-expression functions
When a function's body is one expression, replace the braces and return with =:
fun area(width: Double, height: Double) = width * height
fun isAdult(age: Int) = age >= 18
fun label(score: Int) = if (score >= 50) "Pass" else "Fail"Kotlin infers the return type from the expression. For public functions that other code relies on, writing the return type explicitly is still good practice: fun area(w: Double, h: Double): Double = w * h.
Default and named arguments
Give a parameter a default value and callers can leave it out:
fun order(item: String, size: String = "medium", sugar: Int = 0) =
"$size $item with $sugar sugar"
order("tea") // medium tea with 0 sugar
order("coffee", "large") // large coffee with 0 sugar
order("tea", sugar = 2) // medium tea with 2 sugarNamed arguments (sugar = 2) say which parameter each value is for. They let you skip parameters with defaults, and they make calls with several similar values, like createUser("Ana", true, false), much easier to understand: createUser("Ana", isAdmin = true, sendEmail = false).
vararg, Pair and local functions
vararg accepts any number of arguments, which arrive as an array:
fun average(vararg numbers: Int): Double = numbers.average()
average(4, 8, 9)To return two values, return a Pair with to, and unpack it with destructuring: val (low, high) = minMax(list). For three or more values, a data class is clearer.
Functions can also be declared inside other functions when they're only useful there, keeping helper code close to where it's used.
Passing a list to a vararg parameter
A vararg parameter wants separate values, so passing a whole list doesn't work directly. Put the values in an array and add the spread operator * in front of it:
fun average(vararg numbers: Int): Double = numbers.average()
val scores = listOf(4, 8, 9)
println(average(*scores.toIntArray())) // 7.0
println(average(1, *intArrayOf(2, 3), 10)) // 4.0The * unpacks the array into individual arguments, and you can mix it with ordinary values, as in the second call. For an Int vararg you need an IntArray, which is what toIntArray() gives you; for a String vararg you'd use toTypedArray().
Overloading, recursion and the Unit and Nothing types
Two functions can share a name as long as their parameter types differ. This is called overloading, and Kotlin picks the right version from the arguments:
fun format(price: Double) = "£%.2f".format(price)
fun format(price: Int) = "£$price.00"
format(3.5) // £3.50
format(3) // £3.00Before writing overloads, check whether a default argument would do the same job with a single function. Defaults usually lead to less code.
A function can also call itself. This is recursion, and it suits problems that are defined in terms of a smaller version of themselves:
fun factorial(n: Int): Long = if (n <= 1) 1 else n * factorial(n - 1)
tailrec fun gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)factorial(5) is 120. Every recursive function needs a stopping case, here n <= 1, or it calls itself until the program runs out of stack and crashes with a StackOverflowError. When the recursive call is the very last thing a function does, as in gcd, marking it tailrec lets the compiler turn it into a loop, so it can't overflow the stack.
Two special return types are worth recognising:
| Type | Meaning | Example |
|---|---|---|
Unit | Finishes normally but has no useful result | fun log(msg: String) |
Nothing | Never finishes normally; it always throws | fun fail(msg: String): Nothing = throw IllegalStateException(msg) |
Because Nothing fits anywhere, you can write val n = text.toIntOrNull() ?: fail("Not a number"), and n is still a plain Int.
Kotlin Functions Syntax
Function forms
fun name(a: Int, b: String = "x"): Boolean {
return true
}
fun square(n: Int) = n * n
name(1)
name(1, b = "y")
fun sum(vararg n: Int) = n.sum()
fun bounds(list: List<Int>) = list.min() to list.max()
val (lo, hi) = bounds(listOf(3, 9, 1))Kotlin Functions Summary
fun name(param: Type): ReturnType { }declares a function; Unit means no useful result.- Single-expression functions use
=instead of braces and return. - Default values make parameters optional; named arguments make calls clear.
varargaccepts any number of arguments; Pair and destructuring return two values.- Keep functions small, focused and named after what they do.
Kotlin Functions Examples
Each example shows the code, the output it produces and what each part does.
Functions with return values
fun celsiusToFahrenheit(c: Double): Double {
return c * 9 / 5 + 32
}
fun isFreezing(c: Double) = c <= 0
fun describe(c: Double): String {
val f = celsiusToFahrenheit(c)
val note = if (isFreezing(c)) " (freezing)" else ""
return "$c°C is $f°F$note"
}
fun main() {
println(describe(20.0))
println(describe(-5.0))
}20.0°C is 68.0°F
-5.0°C is 23.0°F (freezing)Line-by-line explanation
- Lines 1 to 3A function with a block body and an explicit return.
- Line 5A single-expression function; the Boolean return type is inferred.
- Lines 7 to 11One function can call others, keeping each one small.
Default and named arguments
fun formatPrice(amount: Double, currency: String = "£", decimals: Int = 2): String =
currency + "%.${decimals}f".format(amount)
fun main() {
println(formatPrice(5.5))
println(formatPrice(5.5, "$"))
println(formatPrice(1234.5678, decimals = 0))
println(formatPrice(decimals = 1, amount = 9.99))
}£5.50
$5.50
£1235
£10.0Line-by-line explanation
- Line 1Two parameters have defaults, so only amount is required.
- Line 2The format string itself is built with a template: "%.2f", "%.0f" and so on.
- Line 7decimals is named, so currency keeps its default.
- Line 8With names, arguments can be given in any order. 9.99 rounds to 10.0 with one decimal.
vararg and returning two values
fun average(vararg scores: Int): Double =
if (scores.isEmpty()) 0.0 else scores.sum().toDouble() / scores.size
fun lowestAndHighest(scores: List<Int>): Pair<Int, Int> =
scores.min() to scores.max()
fun main() {
println(average(70, 85, 90))
println(average())
val (low, high) = lowestAndHighest(listOf(62, 91, 75, 48))
println("Lowest $low, highest $high")
}81.66666666666667
0.0
Lowest 48, highest 91Line-by-line explanation
- Line 1vararg lets callers pass any number of Ints, even none.
- Lines 4 to 5to creates a Pair holding two values.
- Line 11Destructuring unpacks the Pair into two variables at once.
Common Kotlin Functions Mistakes
Missing parameter types
fun add(a, b) = a + b // error: parameters need typesfun add(a: Int, b: Int) = a + bBlock body without return
fun double(n: Int): Int {
n * 2 // error: a 'return' expression required
}fun double(n: Int): Int = n * 2A positional argument after an out-of-order named one
order(size = "large", "tea") // error: mixing named and positioned arguments is not allowedorder(size = "large", item = "tea")Once a named argument is out of its normal position, every argument after it must be named too.
Kotlin Functions in Real Projects
Functions in Kotlin projects
Android code is built from small functions: one to load data, one to format it, one to show it on screen. Kotlin's default and named arguments are used everywhere in Jetpack Compose, Android's modern UI toolkit, where a call like Text("Hi", fontSize = 18.sp, maxLines = 1) sets only the options you care about and leaves the rest at sensible defaults.
Kotlin Functions FAQ
What is the difference between a parameter and an argument in Kotlin?
A parameter is the name and type in the function's declaration, such as name: String in fun greet(name: String). An argument is the actual value you pass when calling it, such as "Ada" in greet("Ada"). Named arguments use the parameter's name to say which value goes where.
Can a Kotlin function return more than one value?
Not directly, but you can return a Pair or Triple and unpack it: val (low, high) = bounds(list). When the values deserve proper names, return a data class instead, such as data class Bounds(val low: Int, val high: Int), which keeps calling code readable.
Do I need to write the return type of a Kotlin function?
For a function with a block body that returns something, yes: without : Type, Kotlin assumes Unit. For a single-expression function written with =, the type is inferred, though writing it out on public functions documents your intent and catches accidental changes.
Can I change a parameter's value inside a Kotlin function?
No. Parameters are read-only, like val, so n = n + 1 inside the function is a compile error. Copy it into a local variable first, for example var count = n, and change that instead.
Where should I put functions that don't belong to a class?
At the top level of a file, outside any class. Kotlin doesn't need a utility class with static methods to hold them. Group related top-level functions in a file with a clear name, such as Pricing.kt, and mark ones that are only used in that file as private.
Kotlin Functions Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the function so it returns the square of n.
square(n: Int): Int = n * n
What does this print?
fun greet(name: String = "friend") = "Hi, $name"
println(greet())
What does this print?
fun box(w: Int = 1, h: Int = 1) = w * h
println(box(h = 5))
What is the return type of a function that only prints?
Which call is valid for fun send(to: String, subject: String = "Hi", urgent: Boolean = false)?
Kotlin Functions Quiz
5 questions to check what you learned.
Which keyword declares a function in Kotlin?
- function
- fun
- def
- void
What does a single-expression function use instead of braces and return?
- =>
- =
- :
- ->
Why use named arguments?
- They run faster
- They're required for every call
- They make parameters mutable
- They make calls clearer and let you skip parameters that have defaults
What does
varargallow?- Passing any number of arguments of that type
- Variable return types
- Changing parameters inside the function
- Calling a function with no name
Can you reassign a parameter inside a Kotlin function, like
n = n + 1?- Yes
- Only for Int parameters
- No, parameters are read-only like val
- Only with default values