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
}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 usedIf the last argument after default parameters is a notes/kotlin/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 = 1You 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
Unitreturn 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 * 3Infix 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 ]] ⇣
In Kotlin, a lambda is an anonymous function that can be treated as a value.
// CharSequence.count(predicate: (Char) -> Boolean)
"A link to the past".count({ letter -> letter == 't' }) // 3
// We can remove the `()` when the last argument is a function
println("A link to the past".count {
letter -> letter == 't'
})
/*
** When using lambda with a single argument,
** you can either give the argument a name or use the `it` keyword
** as a shorthand way to refer to the argument.
*/
"A link to the past".count { it == 't' } // 3💡 A
returninside a lambda expression will return from the enclosing function.
Syntax
// The value of the last expression is implicitly returned.
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
// Removing optional annotations out
val sum = { x: Int, y: Int -> x + y }Anonymous functions
val nums = arrayListOf(1,2,3,4,5)
nums.filter(fun(item) = item > 3)) // [4, 5]💡 A
returnstatement without a label always returns from the function declared with thefunkeyword.
Closures
Kotlin functions can access its closure, which includes the variables declared in the outer scope.
val nums = arrayListOf(1,2,3,4,5)
var sum = 0
nums.filter { it > 0 }.forEach {
sum += it
}
print(sum) // 15Variable 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 `::`