Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 145 additions & 59 deletions docs/topics/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,27 @@ val result = double(2)
Calling member functions uses dot notation:

```kotlin
Stream().read() // create instance of class Stream and call read()
// Creates instance of the Stream class and calls read()
Stream().read()
```

### Parameters

Function parameters are defined using Pascal notation - *name*: *type*. Parameters are separated using commas, and each
parameter must be explicitly typed:
Function parameters are defined using Pascal notation: `name: Type`.
Parameters are separated using commas, and each parameter must be explicitly typed:

```kotlin
fun powerOf(number: Int, exponent: Int): Int { /*...*/ }
```

Inside the body of a function, received parameters are read-only (implicitly `val`):

```kotlin
fun powerOf (number: Int, exponent: Int): Int {
number = 2 // Error: 'val' cannot be reassigned.
}
```

You can use a [trailing comma](coding-conventions.md#trailing-commas) when you declare function parameters:

```kotlin
Expand All @@ -40,10 +49,19 @@ fun powerOf(
) { /*...*/ }
```

### Parameters with default values
This helps with refactorings and code maintenance:
you can move parameters within the declaration without worrying about which is going to be the last one.

Kotlin functions can receive other functions as parameters — and be passed as arguments.
For details, see [](lambdas.md).

### Parameters with default values (optional parameters) {id="parameters-with-default-values"}

Function parameters can have default values, which are used when you skip the corresponding argument.
This reduces the number of overloads:
This reduces the number of necessary overloads.
Parameters with default values are also referred to as _optional parameters_.

A default value is set by appending `=` to the parameter declaration:

```kotlin
fun read(
Expand All @@ -53,48 +71,98 @@ fun read(
) { /*...*/ }
```

Such parameters are also referred to as _optional parameters_.
When you declare a parameter with a default value before a parameter without a default value,
you can only use the default value by [naming arguments](#named-arguments):

A default value is set by appending `=` to the type.
```kotlin
fun foo(
foo: Int = 0,
bar: Int,
) { /*...*/ }

foo(bar = 1) // Uses the default value foo = 0
foo(1) // Error: No value passed for parameter 'bar'
```

[Trailing lambdas](lambdas.md#passing-trailing-lambdas) are an exception to this rule,
since the last parameter must correspond to the passed function:

```kotlin
fun foo(
foo: Int = 0,
bar: () -> Unit,
) { bar() }

foo() { println ("bar") } // Uses the default value for 'foo' and prints "bar"
```

Overriding methods always use the base method's default parameter values.
[Overriding methods](inheritance.md#overriding-methods) always use the base method's default parameter values.
When overriding a method that has default parameter values, the default parameter values must be omitted from the signature:

```kotlin
open class A {
open fun foo(i: Int = 10) { /*...*/ }
open fun foo(i: Int = 10, j: Int = 0) { /*...*/ }
}

class B : A() {
override fun foo(i: Int) { /*...*/ } // No default value is allowed.
// It's not allowed to specify default values here
// but this function also uses 10 for 'i' and 0 for 'j'
// by default.
override fun foo(i: Int, j: Int) { /*...*/ }
}
```

If a parameter with default value precedes a parameter with no default value, the default value can only be used by calling
the function with [named arguments](#named-arguments):
#### Non-constant expressions as default values

You can assign to a parameter a default value that is not constant, as in a function call, or a calculation that uses
values of other arguments, like the `len` parameter in the example above:

```kotlin
fun foo(
bar: Int = 0,
baz: Int,
fun read(
b: ByteArray,
off: Int = 0,
len: Int = b.size,
) { /*...*/ }
```

Parameters referring to other parameters' values must be declared later in the order
(in this example, `len` must be declared after `b`).

foo(baz = 1) // The default value bar = 0 is used
In general default value of a parameter can be any expression.
But such expressions are only calculated when the function is called **without** the corresponding parameter
and a default value needs to be assigned.
For example, this function prints out a line only when it is called without the `print` parameter:

```kotlin
fun read(
b: Int,
print: Unit? = println("No argument passed for 'print'.")
) { println(b) }

fun main() {
read (1) // BothFirst "No argument passed for 'print'.", then "1" is printed
read (1, null) // Only the "1" is printed
}
```

If the last parameter after all parameters with default values has a functional type,
If the last parameter in a function declaration has a functional type,
then you can pass the corresponding [lambda](lambdas.md#lambda-expression-syntax) argument either as a named argument or [outside the parentheses](lambdas.md#passing-trailing-lambdas):

```kotlin
fun foo(
bar: Int = 0,
baz: Int = 1,
foo: Int = 0,
bar: Int = 1,
qux: () -> Unit,
) { /*...*/ }

foo(1) { println("hello") } // Uses the default value baz = 1
foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1
foo { println("hello") } // Uses both default values bar = 0 and baz = 1
// Uses the default value bar = 1
foo(1) { println("hello") }

// Uses both default values foo = 0 and bar = 1
foo(qux = { println("hello") })

// Uses both default values foo = 0 and bar = 1
foo { println("hello") }
```

### Named arguments
Expand Down Expand Up @@ -142,40 +210,29 @@ skipped argument, you must name all subsequent arguments:
reformat("This is a short String!", upperCaseFirstLetter = false, wordSeparator = '_')
```

You can pass a [variable number of arguments (`vararg`)](#variable-number-of-arguments-varargs) with names using the
_spread_ operator (prefix the array with `*`):
You can pass a [variable number of arguments](#variable-number-of-arguments-varargs) (`vararg`) naming the correspoding array:

```kotlin
fun foo(vararg strings: String) { /*...*/ }

foo(strings = *arrayOf("a", "b", "c"))
foo(strings = arrayOf("a", "b", "c"))
```

<!-- Rationale for named arguments interaction with varargs is here https://youtrack.jetbrains.com/issue/KT-52505#focus=Comments-27-6147916.0-0 -->

> When calling Java functions on the JVM, you can't use the named argument syntax because Java bytecode does not
> always preserve the names of function parameters.
>
{style="note"}

### Unit-returning functions

If a function does not return a useful value, its return type is `Unit`. `Unit` is a type with only one value - `Unit`.
This value does not have to be returned explicitly:

```kotlin
fun printHello(name: String?): Unit {
if (name != null)
println("Hello $name")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
```
### Explicit return types

The `Unit` return type declaration is also optional. The above code is equivalent to:
Functions with a block body must always specify return types explicitly, unless it's intended for them to return `Unit`,
[in which case specifying the return type is optional](#unit-returning-functions).

```kotlin
fun printHello(name: String?) { ... }
```
Kotlin does not infer return types for functions with block bodies because such functions may have complex control flow
in the body, and the return type will be non-obvious to the reader (and sometimes even for the compiler).
But the return type is inferred, unless specified, for [single-expression functions](#single-expression-functions).

### Single-expression functions

Expand All @@ -191,13 +248,37 @@ Explicitly declaring the return type is [optional](#explicit-return-types) when
fun double(x: Int) = x * 2
```

### Explicit return types
### Unit-returning functions

Functions with block body must always specify return types explicitly, unless it's intended for them to return `Unit`,
[in which case specifying the return type is optional](#unit-returning-functions).
If a function has a block body and does not return a useful value, its return type is assumed to be `Unit` (corresponds to the `void` type in Java).
`Unit` is a type with only one value - `Unit`.

Kotlin does not infer return types for functions with block bodies because such functions may have complex control flow
in the body, and the return type will be non-obvious to the reader (and sometimes even for the compiler).
You don't have to specify `Unit` as a return type, except for functional type parameters,
and you never have to return `Unit` explicitly.

Therefore, this verbose declaration:

```kotlin
fun printHello(name: String?, aux: () -> Unit): Unit {
if (name != null)
println("Hello $name")
else
println("Hi there!")
return Unit
}
```

can be shortened to:

```kotlin
// The declaration of 'aux' still needs an explicit return type
fun printHello(name: String?, aux: () -> Unit) {
if (name != null)
println("Hello $name")
else
println("Hi there!")
}
```

### Variable number of arguments (varargs)

Expand Down Expand Up @@ -252,7 +333,7 @@ for the call). Infix functions must meet the following requirements:
no [default value](#parameters-with-default-values).

```kotlin
infix fun Int.shl(x: Int): Int { ... }
infix fun Int.shl(x: Int): Int { /*...*/ }

// calling the function using the infix notation
1 shl 2
Expand All @@ -275,7 +356,7 @@ infix fun Int.shl(x: Int): Int { ... }
{style="note"}

Note that infix functions always require both the receiver and the parameter to be specified. When you're
calling a method on the current receiver using the infix notation, use `this` explicitly. This is required to ensure
calling a method on the current receiver using the infix notation, use `this` explicitly. This ensures
unambiguous parsing.

```kotlin
Expand All @@ -292,9 +373,9 @@ class MyStringCollection {

## Function scope

Kotlin functions can be declared at the top level in a file, meaning you do not need to create a class to hold a function,
which you are required to do in languages such as Java, C#, and Scala ([top level definition is available since Scala 3](https://docs.scala-lang.org/scala3/book/taste-toplevel-definitions.html#inner-main)). In addition
to top level functions, Kotlin functions can also be declared locally as member functions and extension functions.
Kotlin functions can be declared at the top level in a file, meaning you do not need to create a class to hold a function
(unlike Java, for example).
Functions can also be declared locally as _member functions_ and _extension functions_.

### Local functions

Expand Down Expand Up @@ -363,32 +444,37 @@ When a function is marked with the `tailrec` modifier and meets the required for
the recursion, leaving behind a fast and efficient loop based version instead:

```kotlin
import kotlin.math.cos
import kotlin.math.abs

val eps = 1E-10 // "good enough", could be 10^-15

tailrec fun findFixPoint(x: Double = 1.0): Double =
if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))
if (abs(x - cos(x)) < eps) x else findFixPoint(cos(x))
```

This code calculates the `fixpoint` of cosine, which is a mathematical constant. It simply calls `Math.cos` repeatedly
This code calculates the fixed point of cosine (a mathematical constant). It simply calls `cos()` repeatedly
starting at `1.0` until the result no longer changes, yielding a result of `0.7390851332151611` for the specified
`eps` precision. The resulting code is equivalent to this more traditional style:

```kotlin
import kotlin.math.cos
import kotlin.math.abs

val eps = 1E-10 // "good enough", could be 10^-15

private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (Math.abs(x - y) < eps) return x
x = Math.cos(x)
val y = cos(x)
if (abs(x - y) < eps) return x
x = cos(x)
}
}
```

To be eligible for the `tailrec` modifier, a function must call itself as the last operation it performs. You cannot use
tail recursion when there is more code after the recursive call, within `try`/`catch`/`finally` blocks, or on open functions.
Currently, tail recursion is supported by Kotlin for the JVM and Kotlin/Native.

**See also**:
* [Inline functions](inline-functions.md)
Expand Down