What is the difference between a bound reference like `instance::method` and an unbound reference like `Type::method` in Kotlin?
answer
- Bound = receiver captured; unbound = receiver is first param
- Unbound arity = bound arity + 1
- instance::length is () -> Int; String::length is (String) -> Int
- Unbound fits map/filter; bound fits a fixed object callback
- :: is the callable-reference operator
basics
~10 sA bound reference already remembers which object to call the method on, so you just pass arguments. An unbound reference does not; you must pass the object itself as the first argument.
solid answer
~40 sA bound reference (`instance::method`) captures a specific receiver object at the point you create it. When you invoke it you only supply the method's own parameters. An unbound reference (`Type::method`) captures no receiver; the receiver becomes an extra leading parameter, so calling it requires passing the instance first. This shows up in the function type: for `String::length`-style member access the unbound form is `(String) -> Int`, while a bound `someString::length` would be `() -> Int`. Bound references are handy for passing an existing object's method as a callback; unbound references are ideal for `map`/`filter` over a collection of objects (`list.map(String::uppercase)`), where each element is fed in as the receiver.
code
kotlin · 5 linesval s = "hi"
val bound: () -> Int = s::length // () -> Int
val unbound: (String) -> Int = String::length // (String) -> Int
println(bound()) // 2
println(unbound("hello")) // 5go deeper
Can state that bound captures the object and unbound needs it passed in; recognizes String::uppercase in map.
Articulates the arity difference and writes the correct function types for both forms.
Explains why the receiver promotes to the first parameter and picks the right form for callbacks vs collection pipelines.
Relates this to overload resolution, expected-type inference, and how the compiler picks bound vs unbound when both could fit.
## What a callable reference is A *callable reference* is a value that points to a function, method, property, or constructor, written with the `::` operator. You can store it, pass it as an argument, and invoke it later. The key question for member functions is: *does the reference already know which object (the receiver) to operate on?* ## Bound reference — receiver is captured Writing `instance::method` produces a **bound** reference. The object on the left (`instance`) is captured and remembered. When you call the reference, you pass only the method's declared parameters — the receiver is already fixed. ```kotlin val name = "Kotlin" val getLength: () -> Int = name::length // bound to `name` println(getLength()) // 6, no argument needed val append: (String) -> String = name::plus // bound; plus takes 1 arg println(append("!")) // "Kotlin!" ``` `name::length` has type `() -> Int` because the receiver is already supplied. ## Unbound reference — receiver is a parameter Writing `Type::method` produces an **unbound** reference. No receiver is captured; instead the receiver is promoted to the **first parameter** of the resulting function type. ```kotlin val lengthOf: (String) -> Int = String::length // unbound println(lengthOf("Kotlin")) // 6, must pass the String val isBlank: (String) -> Boolean = String::isBlank println(isBlank(" ")) // true ``` Here `String::length` has type `(String) -> Int` — one more parameter than the bound form. ## Why arity differs by exactly one The receiver always has to come from *somewhere*. A bound reference gets it from the captured object, so it disappears from the parameter list. An unbound reference gets it from the caller, so it appears as a leading parameter. That is why the unbound form has **arity = bound arity + 1**. ## Typical usage with `map` / `filter` Unbound references shine when each collection element should become the receiver: ```kotlin listOf("a", "bb", "").filter(String::isNotEmpty) // [a, bb] listOf("a", "b").map(String::uppercase) // [A, B] ``` `filter` expects `(String) -> Boolean`, which is exactly the unbound arity. A `::isEmpty`-style member reference written as `String::isEmpty` is `(String) -> Boolean`. ## Bound to `this` Inside a class you can write `this::method` (or just `::method` for top-level functions in the same file scope), which is bound to the current instance. ## Summary table - Bound: `obj::m` → receiver captured → type drops the receiver param. - Unbound: `Type::m` → receiver is first param → type gains a leading param.
- Which form would you pass to `list.map(...)` to uppercase every string, and why?The unbound `String::uppercase`, because `map` needs `(String) -> String` and the unbound reference takes each element as its receiver/first argument.
- What is the function type of `"abc"::get`?`(Int) -> Char` — it is bound to the string, so only the index parameter remains.
A bound reference is a pre-addressed envelope (receiver filled in); an unbound reference is a blank envelope where you write the address (receiver) every time you send it.
saying these in an interview costs you the question
- Saying both forms have the same arity
- Claiming Type::method captures an instance
- Thinking instance::method needs you to pass the instance again
- Confusing `::` with the safe-call `?.` operator
- Believing unbound references only work for top-level functions