diff --git a/docs/topics/functions.md b/docs/topics/functions.md index 37bef4a828d..6d259dbed31 100644 --- a/docs/topics/functions.md +++ b/docs/topics/functions.md @@ -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 @@ -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( @@ -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 @@ -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")) ``` + + > 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 @@ -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) @@ -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 @@ -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 @@ -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 @@ -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)