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
// 15

Practical 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

Leave a Comment

Your email address will not be published. Required fields are marked *