Explain the difference between a bound and an unbound function reference, using `String::length` versus `"hello"::length` as examples. How do their types differ?
answer
- Unbound: receiver = first param
- Bound: receiver captured, zero extra args
- Type::member vs instance::member
- Bound = KFunction0, Unbound = KFunction1
- Bound receiver captured eagerly, like a lambda
basics
~20 sAn unbound reference (String::length) leaves the object out, so you must pass it in as the first argument. A bound reference ("hello"::length) already fixes the object, so you just invoke it with no extra argument.
solid answer
~30 sWith an **unbound** reference `String::length`, the receiver is not supplied at reference-creation time, so it becomes the first parameter: the value behaves like `(String) -> Int` (a `KFunction1<String, Int>`). With a **bound** reference `"hello"::length`, the receiver is captured *now*, so it behaves like `() -> Int` (a `KFunction0<Int>`) and is invoked with no arguments — it always reports `5`. Binding is also how you reference a method on an existing instance: `obj::method` or `this::method`. The bound receiver is evaluated and captured eagerly when the reference expression runs, exactly like capturing a variable in a lambda.
code
kotlin · 13 linesfun main() {
val unbound: (String) -> Int = String::length
val bound: () -> Int = "hello"::length
println(unbound("hi")) // 2 — receiver passed in
println(bound()) // 5 — receiver was captured
// eager capture proof
var sb = StringBuilder("ab")
val r = sb::length
sb = StringBuilder("abcd")
println(r()) // 2, original instance
}go deeper
Recognizes instance::m already knows its object while Type::m needs one passed in.
States the arity/type difference (KFunction0 vs KFunction1) and that the receiver becomes the first param when unbound.
Explains eager capture semantics and chooses bound vs unbound deliberately for callbacks vs collection ops.
Reasons about capture lifetime/leaks (bound refs holding instances) and API design implications of exposing bound callbacks.
## The core distinction A member function needs a **receiver** — the object it runs on (`this` inside the method). A function reference can either *carry* that receiver (**bound**) or *leave it open* (**unbound**). ### Unbound: `Type::member` The receiver is omitted, so it slides into the parameter list as the **first argument**: ```kotlin val len: (String) -> Int = String::length // KFunction1<String, Int> len("hello") // 5 — you pass the receiver explicitly listOf("a", "bb").map(String::length) // each element is the receiver ``` ### Bound: `instance::member` The receiver is **captured eagerly** when the reference is created, so it disappears from the parameter list: ```kotlin val s = "hello" val len: () -> Int = s::length // KFunction0<Int> len() // 5 — receiver already fixed to "hello" ``` `this::method` binds the current object; `obj::method` binds whatever `obj` evaluates to. ## Type comparison | Reference | Behaves as | Reflection type | |----------------------|----------------|---------------------------| | `String::length` | `(String)->Int`| `KFunction1<String, Int>` | | `"hello"::length` | `()->Int` | `KFunction0<Int>` | The bound form has **one fewer parameter** because the receiver is pre-filled. ## Eager capture The bound receiver is evaluated *once*, at the point the `::` expression runs — just like a captured variable: ```kotlin var x = StringBuilder("ab") val ref = x::length // captures the current StringBuilder instance x = StringBuilder("abcd") ref() // 2 — still the original instance, not the new x ``` ## When each is useful - **Unbound** shines in `map`/`filter` where each element supplies the receiver. - **Bound** is for passing 'this specific object's method' as a callback, e.g. `register(listener::onEvent)`.
- When is the bound receiver evaluated?Eagerly, when the `::` expression is evaluated — the instance is captured once, like a closure variable. Reassigning the source variable afterward does not affect the reference.
- How do you bind to the current object inside a class?Use `this::method`, which captures the enclosing instance as the receiver and produces a zero-receiver-parameter reference.
Unbound is a generic 'measure length' tool you hand any string to; bound is 'measure THIS string's length' with the string already loaded in.
saying these in an interview costs you the question
- Saying bound and unbound references have the same arity
- Claiming the bound receiver is re-evaluated on each invocation
- Thinking `Type::member` automatically picks some default instance
- Confusing bound references with extension functions
- Not realizing unbound member references add the receiver as the first parameter