Kotlin Function References
A function reference is a way to treat a function as a value using the :: operator. Instead of wrapping a function in a lambda, you reference it directly and pass it around. This makes code shorter and more readable, especially when a lambda just calls one existing function.
The Problem Function References Solve
fun double(n: Int) = n * 2
val numbers = listOf(1, 2, 3, 4, 5)
// Using a lambda wrapper — verbose
val result1 = numbers.map { n -> double(n) }
// Using a function reference — concise and direct
val result2 = numbers.map(::double)
println(result1) // [2, 4, 6, 8, 10]
println(result2) // [2, 4, 6, 8, 10]Types of Function References
:: operator creates a reference to:
::functionName → top-level function
object::methodName → instance method bound to an object
ClassName::methodName → unbound member function (instance passed as first arg)
ClassName::new → constructor reference
Top-Level Function Reference
fun isEven(n: Int) = n % 2 == 0
fun square(n: Int) = n * n
fun greet(name: String) = println("Hello, $name!")
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
println(numbers.filter(::isEven)) // [2, 4, 6, 8]
println(numbers.map(::square)) // [1, 4, 9, 16, 25, 36, 49, 64]
listOf("Alice", "Bob", "Carol").forEach(::greet)
// Hello, Alice!
// Hello, Bob!
// Hello, Carol!
}Member Function Reference
val words = listOf("kotlin", "java", "python", "go")
// String::uppercase is a reference to the uppercase() method of String
println(words.map(String::uppercase)) // [KOTLIN, JAVA, PYTHON, GO]
println(words.filter(String::isNotBlank)) // [kotlin, java, python, go]
println(words.sortedWith(compareBy(String::length)))
// [go, java, kotlin, python]Bound Instance Reference
class Formatter(private val prefix: String) {
fun format(text: String) = "$prefix: $text"
}
val formatter = Formatter("INFO")
// Bound to the specific formatter object
val formatFn = formatter::format
println(formatFn("App started")) // INFO: App started
println(formatFn("User logged in")) // INFO: User logged in
val messages = listOf("Task done", "Error found", "Retry...")
println(messages.map(formatter::format))
// [INFO: Task done, INFO: Error found, INFO: Retry...]Constructor Reference
data class User(val name: String)
data class Product(val name: String, val price: Double)
fun main() {
val names = listOf("Alice", "Bob", "Carol")
// ::User is a reference to the User constructor
val users = names.map(::User)
println(users) // [User(name=Alice), User(name=Bob), User(name=Carol)]
val pairs = listOf(Pair("Laptop", 75000.0), Pair("Mouse", 800.0))
val products = pairs.map { (name, price) -> Product(name, price) }
println(products)
}Storing a Function Reference
fun add(a: Int, b: Int) = a + b
fun multiply(a: Int, b: Int) = a * b
val operation: (Int, Int) -> Int = ::add
println(operation(3, 4)) // 7
val ops = listOf(::add, ::multiply)
ops.forEach { op -> println(op(5, 3)) }
// 8
// 15Practical Example: Data Pipeline with References
data class RawRecord(val id: String, val value: String)
data class ParsedRecord(val id: Int, val value: Double)
fun isValidRecord(r: RawRecord) = r.id.all { it.isDigit() } && r.value.toDoubleOrNull() != null
fun parseRecord(r: RawRecord) = ParsedRecord(r.id.toInt(), r.value.toDouble())
fun isPositive(r: ParsedRecord) = r.value > 0
fun main() {
val raw = listOf(
RawRecord("1", "99.5"),
RawRecord("abc", "10.0"), // invalid id
RawRecord("2", "-5.0"),
RawRecord("3", "150.0"),
RawRecord("4", "xyz") // invalid value
)
val result = raw
.filter(::isValidRecord)
.map(::parseRecord)
.filter(::isPositive)
result.forEach { println("ID ${it.id}: ${it.value}") }
}Output:
ID 1: 99.5
ID 3: 150.0