Kotlin Functions: Core Concepts and Syntax
Function Declaration
Kotlin uses the fun keyword to define functions:
fun triple(number: Int): Int {
return 3 * number
}
Function Invocation
Call standard functions using conventional syntax:
val product = triple(4)
Call member functions with dot notation:
FileReader().read() // Create FileReader instance and invoke read()
Parameters
Parameters follow Pascal notation (name: type) and require explicit types, separated by commas:
fun calculatePower(base: Int, exponent: Int) { /* implementation */ }
Default Parameters
Parameters can have default values, reducing overload count. Define defaults with = after the type:
fun write(buffer: Array<Byte>, offset: Int = 0, length: Int = buffer.size) { /* implementation */ }
Overriding methods must inherit base method defaults and omit explicit defaults in signatures:
open class Base {
open fun greet(name: String = "Guest") { /* implementation */ }
}
class Derived : Base() {
override fun greet(name: String) { /* implementation */ } // No default here
}
Named Arugments
Improve readability for functions with many or default parameters by specifying names:
fun reformatText(text: String,
lowercase: Boolean = true,
splitCamelCase: Boolean = false,
separator: Char = ' ')
{ /* implementation */ }
// Use defaults for most parameters
reformatText(text = "SampleText", separator = '_')
Position arguments must precede named arguments, and you can spread vararg arrays with *:
fun combineStrings(vararg parts: String) { /* implementation */ }
val segments = arrayOf("first", "second")
combineStrings("prefix", *segments, "suffix")
Unit-Returning Functions
Functions returning no meaningful value have type Unit (implied if omitted):
fun logMessage(message: String?) {
if (message != null)
println("Log: $message")
else
println("Log: Empty message")
// return Unit or return is optional
}
Single-Expression Functions
Omit braces for functions with a single expression, using = for the body. Return type is optional if inferable:
fun quadruple(value: Int): Int = value * 4
fun square(value: Int) = value * value
Explicit Return Types
Block-body functions require explicit return types (except Unit). The compiler doesn’t infer complex flow return types:
fun findMax(numbers: List<Int>): Int {
var max = Int.MIN_VALUE
for (num in numbers) {
if (num > max) max = num
}
return max
}
Varargs
Mark the last (or only) parameter with vararg to accept variable arguments:
fun createList(vararg elements: String): List<String> {
val list = mutableListOf<String>()
for (elem in elements) {
list.add(elem)
}
return list
}
Infix Notation
Functions marked infix (member/extension, single parameter, no vararg/default) can be called without dots/braces:
infix fun Int.multiplyBy(factor: Int): Int { /* implementation */ }
// Valid calls
6 multiplyBy 3
6.multiplyBy(3)
Local Functions
Define functions inside other functions to encapsulate logic and access closures:
fun traverseTree(tree: Tree) {
val visited = mutableSetOf<Node>()
fun dfs(node: Node) {
if (!visited.add(node)) return
for (child in node.children) {
dfs(child)
}
}
dfs(tree.root)
}
Generic Functions
Add type parameters with <> before the function name:
fun <T> wrapInList(item: T): List<T> { /* implementation */ }
Tail-Recursive Funtcions
Optimize recursive functions with tailrec (JVM/Native support), which replaces recursion with loops to avoid stack overflow:
const val PRECISION = 1E-10
// Calculates cosine's fixed point
fun findFixedPoint(start: Double = 1.0): Double {
tailrec fun loop(current: Double): Double {
val next = Math.cos(current)
if (Math.abs(current - next) < PRECISION) return current
return loop(next)
}
return loop(start)
}