skip to content

Explain map-backed delegation: how does `val name: String by map` work, what key is used, and how does delegating to a `MutableMap` differ?

level: middleimportance: should knowfreq 42%

answer

  1. Map/MutableMap have stdlib getValue/setValue extensions
  2. Key = property.name (exact source name)
  3. Read uses getValue → missing key throws NoSuchElementException
  4. MutableMap enables var (writes map[name]=value)
  5. withDefault {} to get null instead of throwing

basics

~20 s

A 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 s

Kotlin'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 lines
kotlin
class 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)         // 3

go deeper

for a junior

Knows by map looks up the property name as the key for reads.

for a middle

Explains read vs. mutable-map write, that the read uses getValue (throws on missing), and the use case.

for a senior

Knows the extension-function mechanism, withDefault for nulls, and the ClassCastException/refactor-fragility caveats.

for a principal

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

context