skip to content

How do indexing (`a[i]`) and call (`a()`) syntax map to functions via operator overloading? Show `get`, `set`, and `invoke`.

level: middleimportance: must knowfreq 60%

answer

  1. a[i] -> get, a[i]=v -> set (value last)
  2. a() -> invoke
  3. Multiple indices allowed
  4. invoke makes objects callable / powers lambdas
  5. All need `operator`

basics

~10 s

a[i] calls a.get(i), and a[i] = v calls a.set(i, v). a() calls a.invoke(). Each must be declared with the operator keyword.

solid answer

~30 s

Index access maps to `get`/`set`: `a[i]` becomes `a.get(i)`; `a[i] = v` becomes `a.set(i, v)` where the assigned value is the **last** parameter. Both support multiple indices — `a[i, j]` is `a.get(i, j)`. The call operator maps to `invoke`: `a()` is `a.invoke()`, `a(x)` is `a.invoke(x)`. `invoke` is what makes lambdas and function types callable and underpins functional-style APIs and DSL builders. All must carry the `operator` modifier. `get` and `invoke` can return any type; `set` typically returns `Unit`. These can be members or `operator` extensions, letting you add index/call syntax to existing types.

code

kotlin · 10 lines
kotlin
class Config {
    private val map = mutableMapOf<String, String>()
    operator fun get(key: String) = map[key]
    operator fun set(key: String, value: String) { map[key] = value }
    operator fun invoke(block: Config.() -> Unit) = apply(block)
}

val c = Config()
c { this["host"] = "localhost" }  // invoke with builder lambda
println(c["host"])                // get -> localhost

go deeper

for a junior

Knows a[i]->get and a()->invoke at a basic level.

for a middle

Correctly states value-last for set, multi-index support, and what invoke enables.

for a senior

Connects invoke to function types/lambdas and uses it for DSLs/factories; knows augmented-assignment desugaring touches get+set.

for a principal

Designs callable/indexable abstractions deliberately (e.g. typed config, DI containers) and weighs clarity vs. cleverness.

## Indexed access: `get` and `set` Kotlin maps square-bracket syntax to the `get` and `set` functions. - **Read:** `a[i]` -> `a.get(i)` - **Write:** `a[i] = v` -> `a.set(i, v)` - **Multiple indices:** `a[i, j]` -> `a.get(i, j)`; `a[i, j] = v` -> `a.set(i, j, v)` For `set`, the **assigned value is always the last argument**. The index arguments come first, in order. ```kotlin class Grid(val w: Int, val h: Int) { private val cells = IntArray(w * h) operator fun get(x: Int, y: Int): Int = cells[y * w + x] operator fun set(x: Int, y: Int, value: Int) { cells[y * w + x] = value } } val g = Grid(3, 3) g[1, 2] = 7 // g.set(1, 2, 7) println(g[1, 2]) // g.get(1, 2) -> 7 ``` `get` may return any type. `set` conventionally returns `Unit`. ## The call operator: `invoke` Declaring `operator fun invoke(...)` makes an instance **callable like a function**: - `a()` -> `a.invoke()` - `a(x)` -> `a.invoke(x)` - `a(x, y)` -> `a.invoke(x, y)` ```kotlin class Adder(val base: Int) { operator fun invoke(x: Int): Int = base + x } val addTen = Adder(10) println(addTen(5)) // addTen.invoke(5) -> 15 ``` This is the same mechanism behind Kotlin's function types: a lambda `{ x: Int -> x + 1 }` is an object whose `invoke` you call when you write `f(x)`. `invoke` is heavily used for **factory-like objects**, **stateful function objects**, and **DSL builders** (an object that you both call and configure). ## Practical notes - All three (`get`, `set`, `invoke`) require the **`operator`** modifier. - They can be **overloaded** (multiple `invoke`/`get` with different parameter lists) and resolved by normal overload rules. - They can be **extensions**, e.g. `operator fun MyType.get(k: String)` to add index syntax to a type you don't own. - Augmented assignment like `a[i] += v` desugars to `a.set(i, a.get(i).plus(v))` when no `plusAssign` applies — combining get, set, and plus.

  • In `a[i, j] = v`, what is the parameter order passed to `set`?
    `a.set(i, j, v)` — indices first in order, the assigned value last.
  • How does `invoke` relate to Kotlin lambdas?
    Function types implement an `invoke` operator; calling `f(x)` on a lambda is `f.invoke(x)`.

saying these in an interview costs you the question

  • Putting the assigned value first instead of last in `set`
  • Thinking `[]` only supports a single index
  • Not knowing `invoke` underlies callable objects/lambdas
  • Forgetting `operator` on get/set/invoke

context