Functions

This section documents Kotlin 2.4.x on the JVM, as published at kotlinlang.org, which is the reference these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against kotlinlang.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Functions are top-level citizens in Kotlin — a function need not belong to a class (see Lexical Structure and Style) — and Kotlin adds several conveniences over Java’s method declarations that eliminate common sources of boilerplate.

Function Declarations

fun add(a: Int, b: Int): Int {
    return a + b
}

fun greet(name: String) {           // no return type: implicitly Unit (see Basic Types and Variables)
    println("Hello, $name!")
}

Default and Named Arguments

A parameter can declare a default value, letting callers omit it — eliminating most of the reason Java code resorts to overloaded methods:

fun createUser(name: String, isAdmin: Boolean = false, retries: Int = 3) { /* ... */ }

createUser("Ada")                                  // isAdmin=false, retries=3
createUser("Grace", isAdmin = true)                // named argument -- skips "retries", order-independent
createUser(name = "Bo", retries = 5, isAdmin = true) // named arguments may appear in any order

Named arguments (isAdmin = true) can be combined with positional ones (positional arguments must come first) and are especially valuable at a call site with several Boolean/numeric parameters, where positional -only Java code is easy to misread (createUser("Ada", true, 5) — true/5 meaning what, exactly?).

Single-Expression Functions

A function whose body is one expression can drop the braces and return, using = instead:

fun square(x: Int): Int = x * x
fun isEven(n: Int) = n % 2 == 0            // return type inferred as Boolean

This form is used throughout this section wherever a function body is a single expression — it is not a separate feature so much as the natural consequence of if/when already being expressions (Control Flow).

vararg

A parameter marked vararg accepts zero or more arguments, collected as an Array inside the function — Kotlin’s equivalent of Java’s …​:

fun sum(vararg numbers: Int): Int = numbers.sum()

sum(1, 2, 3)                 // 6
sum()                        // 0

val values = intArrayOf(4, 5, 6)
sum(*values)                 // the "spread" operator (*) expands an array into vararg positions

Only one parameter may be vararg, and by convention it is placed last (a later positional parameter is then allowed only via named-argument syntax).

Local Functions

A function may be declared inside another function, nesting arbitrarily and closing over the enclosing function’s local variables — useful for a small helper that has no reason to be visible outside its caller:

fun printFactorial(n: Int) {
    fun factorial(x: Int): Int =           // local function, only visible inside printFactorial
        if (x <= 1) 1 else x * factorial(x - 1)

    println("$n! = ${factorial(n)}")
}

tailrec

Marking a recursive function tailrec tells the compiler to rewrite a tail-recursive call (the recursive call is the very last operation performed) into an equivalent loop, avoiding both the call-stack growth and the StackOverflowError risk that plain recursion carries for large inputs:

tailrec fun factorial(n: Long, accumulator: Long = 1): Long =
    if (n <= 1) accumulator else factorial(n - 1, n * accumulator)

factorial(20)   // compiled as an iterative loop, not a deep call chain

The compiler verifies the function actually is in tail form and emits a warning (not silently ignoring the annotation) if it is not.

See Also