skip to content

How does operator overloading work in Kotlin? Name the function-name conventions for `+`, `[]` get/set, `in`, and `..`, and explain what `operator` does.

level: middleimportance: must knowfreq 60%

answer

  1. operator modifier is mandatory
  2. fixed symbol→name table: plus/get/set/contains/rangeTo/invoke
  3. a[i]=v → set(i, v); value is last arg
  4. in → contains, .. → rangeTo, ..< → rangeUntil
  5. += prefers plusAssign; closed operator set

basics

~20 s

Kotlin maps symbols to specially named functions. + calls plus, a[i] calls get, a[i]=v calls set, x in c calls contains, a..b calls rangeTo. You mark each with the operator keyword so the compiler allows the symbolic call.

solid answer

~40 s

Operators are sugar over conventionally named functions marked with the `operator` modifier. The compiler resolves a fixed symbol → name table: `+` → `plus`, `-` → `minus`, `*` → `times`, `unaryMinus`/`unaryPlus`/`not` for prefix `-x`/`+x`/`!x`, `a[i]` → `get(i)`, `a[i] = v` → `set(i, v)`, `x in c` → `c.contains(x)`, `a..b` → `rangeTo`, `a..<b` → `rangeUntil`, `++`/`--` → `inc`/`dec`, `a += b` → `plusAssign` (or `a = a.plus(b)`), and `a()` → `invoke`. `operator` is mandatory — without it the symbol won't compile, and conversely an `operator`-marked function whose signature doesn't match the convention is rejected. You can overload as member or extension. The set of overloadable operators is closed; you cannot invent new symbols. This is what lets builder DSLs write `config["key"] = value`.

code

kotlin · 9 lines
kotlin
data class V2(val x: Int, val y: Int) {
    operator fun plus(o: V2) = V2(x + o.x, y + o.y)
    operator fun get(i: Int) = if (i == 0) x else y
    operator fun contains(c: Int) = c == x || c == y
}

val v = V2(1, 2) + V2(3, 4)  // V2(4, 6)
val first = v[0]              // 4 via get
val has = 6 in v             // true via contains

go deeper

for a junior

Knows + maps to plus and [] to get/set, and that operator is needed.

for a middle

Recites the convention table including in/../invoke and the set last-argument rule.

for a senior

Explains plusAssign vs plus resolution, compareTo/equals routing, and using extension operators on foreign types.

for a principal

Judges when operator overloading aids a DSL vs. obscures intent, and the cost of surprising operator semantics in a large codebase.

## The mechanism Kotlin does **not** let you define arbitrary symbols. Instead, each operator symbol is hard-wired to a **conventionally named function**, and you opt in by marking that function with the **`operator`** modifier. The compiler then translates the symbol into a call. If you omit `operator`, the symbolic form won't compile. If you add `operator` but the signature doesn't fit the convention (wrong arity/return), the compiler rejects it. ## Key conventions | Expression | Translates to | |---|---| | `a + b` | `a.plus(b)` | | `a - b` | `a.minus(b)` | | `a * b` | `a.times(b)` | | `-a` / `+a` / `!a` | `a.unaryMinus()` / `a.unaryPlus()` / `a.not()` | | `a[i]` | `a.get(i)` | | `a[i] = v` | `a.set(i, v)` | | `a[i, j]` | `a.get(i, j)` | | `x in c` | `c.contains(x)` | | `a..b` | `a.rangeTo(b)` | | `a..<b` | `a.rangeUntil(b)` | | `a()` | `a.invoke()` | | `a++` / `a--` | `a.inc()` / `a.dec()` | | `a += b` | `a.plusAssign(b)` or `a = a.plus(b)` | | `a == b` | `a.equals(b)` (null-safe) | | `a < b` etc. | `a.compareTo(b)` via `Comparable` | ## `get`/`set` for builder syntax ```kotlin class Config { private val map = mutableMapOf<String, String>() operator fun get(key: String): String? = map[key] operator fun set(key: String, value: String) { map[key] = value } } val c = Config() c["host"] = "localhost" // c.set("host", "localhost") println(c["host"]) // c.get("host") ``` `set` always uses the **last** argument(s)' final slot as the assigned value: `a[i] = v` → `set(i, v)`; `a[i, j] = v` → `set(i, j, v)`. ## Rules and gotchas - The operator set is **closed** — you cannot invent `<=>` or `**`. - `==` maps to `equals` and is **null-safe** (`null == null` is true); you don't mark `equals` with `operator` because `Any.equals` already is. - Comparison operators (`<`, `>`, `<=`, `>=`) all route through `compareTo` from `Comparable`, returning `Int`. - `+=` prefers `plusAssign` (mutating) if present; if both `plus` and `plusAssign` apply it's an **ambiguity error**. - Operators can be **extension** functions, enabling DSL operators on types you don't own.

  • What happens if a class defines both `plus` and `plusAssign` and you write `a += b`?
    If both are applicable the compiler reports an ambiguity error; you must pick one. `plusAssign` is for mutable in-place types, `plus` for immutable ones.
  • Can you overload `&&` or `||`?
    No. They are not in the overloadable set and they short-circuit; only `and`/`or` infix functions exist, which do not short-circuit.

saying these in an interview costs you the question

  • Claiming you can define brand-new operator symbols
  • Forgetting the `operator` modifier is required
  • Saying `a[i] = v` calls `get` instead of `set`
  • Thinking `<` maps to a `lessThan` function rather than `compareTo`
  • Believing `&&`/`||` are overloadable

context