Explain map-backed delegation: how does `val name: String by map` work, what key is used, and how does delegating to a `MutableMap` differ?
answer
- Map/MutableMap have stdlib getValue/setValue extensions
- Key = property.name (exact source name)
- Read uses getValue → missing key throws NoSuchElementException
- MutableMap enables var (writes map[name]=value)
- withDefault {} to get null instead of throwing
basics
~20 sA property can delegate to a map; reading the property looks it up in the map by the property's own name. With a mutable map, assigning the property writes that key back into the map.
solid answer
~40 sKotlin's stdlib provides extension `operator fun getValue` on `Map<String, *>` and both `getValue`/`setValue` on `MutableMap<String, *>`. So `val name: String by map` compiles because `Map` itself becomes the delegate: reading `name` calls `map.getValue(this, ::name)`, which looks up the entry whose key equals `property.name` (the source property name, here `"name"`). If the key is missing it throws `NoSuchElementException` (it uses the map's `getValue`, not `get`). With a `MutableMap`, `var name: String by map` also enables writes: `name = "Ann"` calls `map.setValue(...)` which does `map[property.name] = value`. This is handy for parsing JSON-like bags into typed accessors, but lookups are by string name and unchecked at compile time for presence.
code
kotlin · 8 linesclass Settings(val map: MutableMap<String, Any?>) {
var theme: String by map // reads/writes map["theme"]
val version: Int by map // reads map["version"], throws if absent
}
val s = Settings(mutableMapOf("theme" to "dark", "version" to 3))
s.theme = "light" // map["theme"] == "light"
println(s.version) // 3go deeper
Knows by map looks up the property name as the key for reads.
Explains read vs. mutable-map write, that the read uses getValue (throws on missing), and the use case.
Knows the extension-function mechanism, withDefault for nulls, and the ClassCastException/refactor-fragility caveats.
Frames map-backing as a dynamic-typing escape hatch, weighs it against data classes/serialization, and notes type-safety and evolution risks.
## The idea Kotlin lets a `Map` (or `MutableMap`) act directly as a delegate, so you can give a loosely-typed bag of values a typed, property-style facade: ```kotlin class User(map: Map<String, Any?>) { val name: String by map val age: Int by map } val u = User(mapOf("name" to "Ada", "age" to 36)) println(u.name) // Ada println(u.age) // 36 ``` ## How it works under the hood The stdlib declares extension operator functions in `kotlin.collections`: ```kotlin operator fun <V, V1 : V> Map<in String, V>.getValue(thisRef: Any?, property: KProperty<*>): V1 operator fun <V> MutableMap<in String, V>.setValue(thisRef: Any?, property: KProperty<*>, value: V) ``` Because these are **extensions**, the `Map`/`MutableMap` value supplied after `by` satisfies the delegate convention without you writing anything. ## The key is the property name The lookup key is `property.name` — the source name of the delegated property. So `val name by map` reads `map["name"]`. Rename the property and the key changes too. (There is no automatic camelCase/snake_case translation; the key is exactly the property identifier.) ## getValue, not get → missing keys throw The read path uses the map's `getValue` extension semantics: a **missing key throws `NoSuchElementException`**, it does NOT return null. If you want absent-as-null you must back it with a `withDefault` map or store explicit nulls. ```kotlin val m = mapOf<String, Any?>().withDefault { null } val x: String? by m // returns null instead of throwing ``` ## MutableMap enables var With a `MutableMap`, `setValue` writes back: ```kotlin class Editable(val map: MutableMap<String, Any?>) { var name: String by map } val e = Editable(mutableMapOf("name" to "x")) e.name = "y" // map["name"] now == "y" ``` ## Caveats - Type safety is partial: the stored value must be castable to the property type at read time, or you get a `ClassCastException` at the access site. - Lookups are string-keyed, so refactors that rename properties silently change the key. - Useful for dynamic data (JSON, config), but for fixed schemas a data class is clearer.
- What happens if the key is missing when you read a map-delegated val?It throws `NoSuchElementException`, because the read goes through the map's `getValue` (not `get`). Use `withDefault { ... }` to supply a fallback instead.
- Does map delegation translate names (e.g. camelCase to snake_case)?No. The key is exactly `property.name`. Any naming convention mismatch must be handled manually (e.g. pre-transform the map keys).
It's like a typed window onto a dictionary: each property is a labeled pane that always looks up its own name in the underlying map.
saying these in an interview costs you the question
- Saying a missing key returns null (it throws NoSuchElementException)
- Claiming you must write a custom getValue for map delegation (stdlib provides it)
- Thinking the key is something other than the property name
- Believing a plain (read-only) Map supports var delegation
- Assuming full compile-time type safety of stored values