Show a correct `var` extension property with a working setter. What can its `set(value)` legally do, given there's no backing field?
answer
- var needs get() + set(value)
- No field -> setter writes through
- Targets: mutating method / real prop / external map
- Immutable receiver -> can't be var
- get() should see what set() wrote
basics
~10 sA mutable extension property's setter must change something that already exists on the receiver — like calling a method that mutates it. It can't store the value itself because there's no field.
solid answer
~40 sA `var` extension property needs both `get()` and `set(value)`. With no backing field, the setter must **write through** to existing mutable state on the receiver: call a mutating method, assign another (real) property, or update an external store. Example: `var StringBuilder.firstChar: Char` whose `set(value) { this.setCharAt(0, value) }`. The getter reads the same state back (`get() = this[0]`). Common write-through targets: collection/`StringBuilder` mutators, fields of a class you own, or a side `Map`. If the receiver is immutable (like `String`), you simply can't write a meaningful `var` extension — there's nothing to mutate. So the setter is constrained to transforming the *value* into a mutation of the receiver's existing data.
code
kotlin · 7 linesvar StringBuilder.firstChar: Char
get() = this[0]
set(value) { setCharAt(0, value) }
val sb = StringBuilder("cat")
sb.firstChar = 'b'
println(sb) // batgo deeper
Can write get() and set(value) and use the property assignment syntax.
Explains 'write-through' and that the setter must mutate existing receiver state.
Notes immutable-receiver limitation and consistency between get/set; flags external-store hazards.
Weighs whether a mutable extension is appropriate at all vs. an explicit method, and the API-clarity cost of hidden mutation.
## Declaring a writable extension property A `var` extension property requires a getter **and** a setter: ```kotlin var StringBuilder.firstChar: Char get() = this[0] set(value) { this.setCharAt(0, value) } fun main() { val sb = StringBuilder("cat") sb.firstChar = 'b' // calls setFirstChar(sb, 'b') println(sb) // bat } ``` `value` is the incoming assignment (`sb.firstChar = 'b'` -> `value == 'b'`). ## What the setter may legally do Since there is **no backing field** (`field` is illegal), the setter cannot store `value` anywhere by itself. It must **write through** to state that already exists: - Call a **mutating method** on the receiver (`setCharAt`, `add`, `put`, ...). - Assign to a **real member property** of the receiver (one with its own storage). - Update an **external structure** keyed by the receiver (e.g. a `MutableMap<Receiver, V>`). ```kotlin // Write-through to a real member property class Box { var raw: Int = 0 } var Box.doubled: Int get() = raw * 2 set(value) { raw = value / 2 } // Write-through to an external map private val tags = HashMap<Any, String>() var Any.tag: String get() = tags[this] ?: "" set(value) { tags[this] = value } // beware: keeps strong refs / leaks ``` ## When you simply can't If the receiver type is **immutable** (e.g. `String`, a data class field you can't change, a read-only `List`), there is nothing to write through to, so a `var` extension makes no sense — keep it a `val`. ## Consistency expectation Good practice: `get()` should observe whatever `set(value)` wrote, so the property behaves like a normal one. The `Box.doubled` example round-trips: set 10 -> raw=5 -> get returns 10. ## Pitfalls - External-map approaches can **leak memory** (strong keys) and aren't thread-safe by default. - Don't put heavy work or side effects beyond the obvious mutation — surprising setters are a maintenance trap.
- Why can't you write a meaningful `var String.firstChar`?`String` is immutable — there is no mutating operation to write the new character through to, so the setter would have nothing valid to do.
- What's the risk of backing a `var` extension with a global Map?Strong keys keep receivers alive (memory leak) and the map is not thread-safe unless you make it so; entries also never get cleaned up automatically.
saying these in an interview costs you the question
- Trying to assign to `field` in the setter
- Writing a `var` extension on an immutable type
- A setter whose value can't be observed by the getter
- Ignoring leak/thread-safety issues of external-map storage