Functions

Kotlin functions are first-class, which means they can be stored in variables and data structures, and can be passed as arguments to and returned from other higher-order functions. You can perform any operations on functions that are possible for other non-function values.

Anatomy of a Function

private fun obtainQuest(
playerLevel: Int,
hasAllies: Boolean = false, // default argument
): String {
// return quest
}

Visibility Modifiers

If a default parameter precedes a parameter with no default value, the default value can only be used by calling the function with named arguments.

fun foo(
bar: Int = 0,
baz: Int,
) { /*...*/ }
foo(baz = 1) // The default value bar = 0 is used

If the last argument after default parameters is a lambda, you can pass it either as a named argument or outside the parentheses:

fun foo(
bar: Int = 0,
baz: 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

You can pass a variable number of arguments (vararg) with names using the spread operator:

fun foo(vararg strings: String) { /*...*/ }
foo(strings = *arrayOf("a", "b", "c"))

Unit-returning functions

If a function does not return a useful value, its return type is Unit.

fun printHello(name: String): Unit {
println("Hello $name")
}

💡 The Unit return type declaration is optional.

Single-expression functions

When a function returns a single expression, the curly braces can be omitted and the body is specified after a = symbol:

fun double(x: Int): Int = x * 2
// Return type is optional as can be inferred
fun triple(x: Int) = x * 3

Infix notation

Functions marked with the infix keyword can also be called using the infix notation (omitting the dot and the parentheses for the call). Infix functions must meet the following requirements:

  • They must be member functions (functions defined inside a class or object) or [[ extension functions ]].
  • They must have a single parameter.
  • The parameter must not accept a variable number of arguments and must have no default value.
infix fun Int.shl(x: Int): Int { ... }
// calling the function using the infix notation
1 shl 2
// is the same as
1.shl(2)

The function type

A function type definition consists of:

  • The function’s parameters, in parentheses.
  • The return type delimited by ->
val msg: () -> String = {
"Hello World!"
}
val greet: (String) -> String = { to ->
"Hello $to!"
}
// Removing optional types
val msg = { "Hello World!"}
val greet = { to: String -> "Hello $to!" }

The Nothing Type

Similar to the Unit type, Nothing indicates that a function returns no value with the exception that this function is guaranteed to never be successfully complete.

/**
* Always throws [NotImplementedError] stating that operation is not implemented.
*/
public inline fun TODO(): Nothing = throw NotImplementedError()

💡 When a function is marked as inline, the compiler will replace every call to that function with the actual code of the function itself, instead of generating a function call instruction at runtime.

One use for it is, as its name indicate, to note that there is still work to do.

fun shouldReturnAString(): String {
TODO("implement the string building functionality")
}

Backticks function name

This feature is included to support interoperability with other languages and for support expressive names of functions tat are used in a testing file

fun `users should be signed out when they click logout`() {
// Do test
}
// instead of `usersShouldBeSignedOutWhenTheyClickLogout`

lambda

Variable number of arguments (varargs)

You can mark a parameter of a function (usually the last one) with the vararg modifier:

fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
val ary = arrayOf(1, 2)
// We can pass the contents of an array using
// using the `spread` operator by prefixing the
// array with `*`
val list = asList(0, *a, 3) // [0,1,2,3]

Function Reference

A function reference converts a regular function using the fun keyword into a value with a function type.

// simple lambda sum function
val sum: (Int, Int) -> Int = { x, y -> x + y }
// function that accepts an op lambda
fun applyOp(x: Int, y: Int, op: (Int, Int) -> Int): Int = op(x, y)
// normal usage
applyOp(2, 3, sum)
/*
* Refactor to use function references
*/
// We have to use a function as References to variables aren't supported
fun sum(x: Int, y: Int) = x + y
// Error, as is not detected as lambda
applyOp(2, 3, sum) // Function invocation 'sum(...)' expected
applyOp(2, 3, ::sum) // This works as it uses the reference operator `::`

Extension functions