Functions
Function Declarations
Section titled “Function Declarations”Kotlin uses the fun keyword. The return type follows the parameter list, separated by a colon.
fun add(a: Int, b: Int): Int { return a + b}
// Single-expression body -- return type can be inferredfun add(a: Int, b: Int) = a + b
// Unit return type (equivalent to void in Java)fun log(message: String) { println(message)}// Unit is explicit herefun log(message: String): Unit { println(message) }Default Parameters
Section titled “Default Parameters”Parameters can have default values, eliminating the need for method overloading in most cases.
fun connect( host: String, port: Int = 443, timeout: Long = 30_000, useTls: Boolean = true): Connection { // ...}Call with positional arguments or named arguments:
connect("example.com") // all defaultsconnect("example.com", 8080) // port=8080, rest defaultconnect("example.com", timeout = 60_000) // host + timeout, rest defaultconnect(timeout = 60_000, host = "example.com") // any order with named argsMixing Positional and Named Arguments
Section titled “Mixing Positional and Named Arguments”Positional arguments must precede named arguments.
connect("example.com", 8080, useTls = false) // valid// connect(host = "example.com", 8080) // error: named after positionalNamed Arguments
Section titled “Named Arguments”Named arguments improve readability for functions with multiple parameters of the same type.
val rect = createRectangle( width = 100, height = 50, x = 10, y = 20)When calling Java methods from Kotlin, named arguments cannot be used (the JVM does not preserve Parameter names by default; use -parameters compiler flag to enable this).
Extension Functions
Section titled “Extension Functions”Extension functions add functions to existing classes without modifying their source code or using Inheritance. They are resolved statically — they are not virtual.
fun String.isPalindrome(): Boolean { return this == this.reversed()}
fun String.addExclamation(suffix: String = "!"): String { return this + suffix}
println("racecar".isPalindrome()) // trueprintln("hello".addExclamation()) // hello!The receiver type (String in this case) is the type being extended. Inside the function body, this refers to the receiver instance.
Nullable Receiver Extensions
Section titled “Nullable Receiver Extensions”fun String?.orEmpty(): String = this ?: ""
val nullStr: String? = nullprintln(nullStr.orEmpty()) // ""println("hello".orEmpty()) // "hello"Extension Functions vs Utility Functions
Section titled “Extension Functions vs Utility Functions”Extension functions compile to static functions with the receiver as the first parameter. They do Not modify the class.
// Kotlinfun String.isPalindrome() = this == this.reversed()
// Equivalent Javapublic static boolean isPalindrome(String receiver) { return receiver.equals(new StringBuilder(receiver).reverse().toString());}Import and Scope
Section titled “Import and Scope”Extension functions are imported and resolved at compile time based on static types.
// In file StringUtils.ktpackage com.example.util
fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }import com.example.util.capitalizeWords
println("hello world".capitalizeWords())If a member function and an extension function have the same signature, the member always wins.
Infix Functions
Section titled “Infix Functions”Functions marked infix can be called with infix notation (without parentheses and dots) when they Have a single parameter.
infix fun Int.power(exponent: Int): Long { return toLong().pow(exponent)}
val result = 2 power 10 // 1024Standard library examples:
val pair = "key" to "value"val range = 1 until 10val contains = 5 in rangeInfix functions must have exactly one parameter and must be a member function or an extension Function. The parameter cannot accept a variable number of arguments and cannot have a default Value.
Lambda Expressions
Section titled “Lambda Expressions”Lambdas are anonymous functions enclosed in curly braces. The last expression is the return value.
val sum: (Int, Int) -> Int = { a, b -> a + b }val double: (Int) -> Int = { it * 2 }val print: (String) -> Unit = { println(it) }val noArgs: () -> Unit = { println("executed") }Trailing Lambda Syntax
Section titled “Trailing Lambda Syntax”When a function”s last parameter is a function type, the lambda can be placed outside the Parentheses.
list.filter({ it > 0 })list.filter { it > 0 }
list.fold(0) { acc, element -> acc + element }Implicit Parameter: it
Section titled “Implicit Parameter: it”When a lambda has a single parameter, its name defaults to it.
list.filter { it > 0 } // 'it' is the implicit parameterlist.filter { element -> element > 0 } // equivalent with explicit nameDestructuring in Lambdas
Section titled “Destructuring in Lambdas”val map = mapOf("a" to 1, "b" to 2)map.forEach { (key, value) -> println("$key -> $value")}Lambdas with Multiple Statements
Section titled “Lambdas with Multiple Statements”val transform: (Int) -> String = { val doubled = it * 2 val formatted = String.format("%04d", doubled) formatted}Returning from a Lambda
Section titled “Returning from a Lambda”return returns from the enclosing function. Use a labeled return to return from the lambda.
fun findFirstNegative(list: List<Int>): Int? { list.forEach { if (it < 0) return it // returns from findFirstNegative } return null}Higher-Order Functions
Section titled “Higher-Order Functions”Functions that take functions as parameters or return functions.
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b)}
val result = operate(3, 4) { x, y -> x * y } // 12Function Types
Section titled “Function Types”Function types use the parenthesized parameter list followed by -> and the return type.
val noParams: () -> Unit = { }val oneParam: (Int) -> Boolean = { it > 0 }var twoParams: (String, Int) -> String = { s, i -> s.repeat(i) }val nullableFunction: ((Int) -> String)? = nullval functionReturningFunction: (Int) -> (Int) -> Int = { a -> { b -> a + b } }Returning Functions
Section titled “Returning Functions”fun multiplier(factor: Int): (Int) -> Int { return { number -> number * factor }}
val triple = multiplier(3)println(triple(5)) // 15Inline Functions
Section titled “Inline Functions”Higher-order functions create anonymous classes for lambdas, causing allocation overhead. The inline modifier instructs the compiler to substitute the function body and the lambda at each call Site.
inline fun measureTime(block: () -> Unit): Long { val start = System.nanoTime() block() return System.nanoTime() - start}noinline prevents specific lambda parameters from being inlined (needed when the lambda is stored Or passed to a non-inline function):
inline fun runAndStore(block: () -> Unit, noinline later: () -> Unit) { block() storeLater(later)}crossinline prevents non-local returns inside the lambda (required when the lambda is called in a Different execution context, such as inside another lambda or an object expression).
Local Functions
Section titled “Local Functions”Functions can be declared inside other functions. They have access to the enclosing scope.
fun processUser(user: User) { fun validate(name: String) { require(name.isNotBlank()) { "Name must not be blank" } }
validate(user.name) validate(user.email)}Common Pitfalls
Section titled “Common Pitfalls”- ** Using
itimplicitly when the lambda context is unclear. Name the parameter explicitly when nesting lambdas or whenitwould be ambiguous. - ** Capturing mutable variables in lambdas. Each captured variable is boxed, which adds allocation overhead in loops.
- ** Forgetting that extension functions are resolved statically. If you call an extension function on a variable declared as a supertype, the extension for the supertype is resolved, not the actual runtime type.
- ** Using
inlineon large functions or functions with many lambda parameters. This increases code size and can cause longer compilation times. Reserveinlinefor small, frequently called functions where the allocation overhead matters.
Summary
Section titled “Summary”This topic covers the mathematical techniques and concepts related to functions, including key theorems, methods, and problem-solving approaches.
Key concepts include:
- fundamental definitions and theorems
- algebraic and graphical methods
- proof and logical reasoning
- problem-solving strategies
- applications and modelling
Regular practice with a variety of question types is essential to build fluency and confidence in applying these mathematical techniques.
Intuition
Section titled “Intuition”Functions in Kotlin are like recipes — they take ingredients (parameters), follow steps (body), and produce a result (return value). Extension functions are like adding new recipes to an existing cookbook without rewriting it. Higher-order functions are recipes that accept other recipes as ingredients, enabling patterns like filtering, mapping, and transformation. The inline keyword is a performance optimisation that avoids creating anonymous classes for each lambda call — think of it as baking the recipe directly into the calling code rather than passing a separate instruction card. Default parameters eliminate method overloading, keeping the API surface clean.
Worked Examples
Section titled “Worked Examples”Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.