When is the receiver of a bound reference captured, and how do `this::method` and bound references to a `var` behave over time?
answer
- Bound capture is eager, at creation, like a closure
- Reassigning the var does NOT retarget the reference
- Identity frozen, but mutable state is read fresh each call
- this::method binds the current instance
- Bound refs can leak the captured receiver
basics
~10 sThe object is captured the moment you write the bound reference, not when you call it. If you later reassign the variable, the reference still points at the original object.
solid answer
~50 sA bound reference evaluates its left-hand expression eagerly, at reference-creation time, and stores that object. Later reassigning the source `var` does not retarget the reference — it still holds the original instance, just like a captured value in a closure. `this::method` binds to the current `this` at that point (useful in classes or to pass an instance method as a callback). Because the receiver is fixed, a bound reference's invocation reads the captured object's *current state* each call (state can mutate), but never switches to a different object. Contrast unbound `Type::method`, which captures nothing and dispatches on whatever instance is passed at call time — so it naturally reflects different objects and polymorphic overrides per call. This distinction matters for callbacks stored long-term: a bound reference can pin an object alive (a potential leak) and freeze identity, whereas an unbound reference stays object-agnostic.
code
kotlin · 4 linesvar s = "first"
val ref = s::length
s = "second-longer"
println(ref()) // 5 (original "first"), not 13go deeper
Recognizes the object is captured when the reference is written.
Explains that reassigning the source variable does not change the reference's target.
Distinguishes frozen identity from mutable state, explains this::method, and flags the leak risk versus unbound references.
Reasons about lifetime/GC implications in listener architectures and how bound vs unbound choice affects polymorphic dispatch and API design for callbacks.
## Capture is eager The expression to the left of `::` in a bound reference is evaluated **immediately** when the reference value is created, and the resulting object is captured — analogous to capturing a value in a lambda/closure. ```kotlin var s = "first" val ref = s::length // captures the "first" String object now s = "second-longer" // reassign the var println(ref()) // 5 -> still the original "first", not 13 ``` Reassigning `s` does not change what `ref` points to. The reference holds the object, not the variable slot. ## State vs identity The captured *identity* is frozen, but if that object is mutable, each invocation observes its current state: ```kotlin class Counter { var c = 0; fun value() = c } val counter = Counter() val read = counter::value // bound to this Counter counter.c = 42 println(read()) // 42 -> same object, new state ``` ## `this::method` Inside a class, `this::method` binds to the current instance — handy for handing an instance method to a higher-order function or registering a callback: ```kotlin class Service(private val log: (String) -> Unit) { fun handle(msg: String) = log("[svc] $msg") fun register(bus: Bus) = bus.subscribe(this::handle) // bound to this Service } ``` The subscription will always invoke *this* `Service`'s `handle`. ## Polymorphism: bound vs unbound dispatch A bound reference dispatches on its captured object, so virtual overrides resolve against that object's runtime type. An unbound reference dispatches on whatever instance is passed at call time: ```kotlin open class A { open fun who() = "A" } class B : A() { override fun who() = "B" } val unbound: (A) -> String = A::who println(unbound(B())) // "B" -> virtual dispatch on the passed instance val a: A = B() val bound = a::who println(bound()) // "B" -> dispatch on the captured B ``` Both respect virtual dispatch, but the unbound form lets the *caller* vary the instance per call. ## Lifetime / leak caveat Because a bound reference holds a strong reference to its receiver, storing it long-term (e.g., in an event bus) keeps that object alive — a classic source of memory leaks in listener/observer code. Unbound references hold no instance, so they cannot leak a receiver. ## Summary - Bound: receiver captured eagerly; identity frozen; state may mutate; can pin object alive. - `this::m`: bound to current `this`. - Unbound: no capture; instance chosen at each call; ideal for polymorphic, object-agnostic use.
- Why can storing a bound reference in a long-lived event bus cause a memory leak?The bound reference holds a strong reference to the captured receiver, keeping it (and its object graph) alive as long as the bus retains the reference. An unbound reference holds no receiver and avoids that.
- If the captured object's mutable field changes, does the bound reference's result change?Yes. The identity is fixed but state is read at call time, so it reflects the latest field value of that same object.
saying these in an interview costs you the question
- Saying the bound reference re-reads the variable on each call
- Claiming reassigning the var retargets the reference
- Thinking bound references ignore virtual dispatch
- Ignoring the leak risk of long-lived bound callbacks
- Confusing frozen identity with frozen state