Phase 5 · Advanced KotlinModule 24~32 min read

Operator Overloading & Conventions

Give your types natural syntax with operator functions and infix conventions.

What you'll learn

Kotlin lets your own types use natural syntax — a + b, grid[0, 1], 3 in range — through operator overloading and conventions. Used well, it makes domain code read beautifully.

By the end you'll be able to:

  • Overload operators like + and * with operator fun
  • Use the get/set conventions for indexing
  • Write infix functions for readable calls
  • Know when not to overload

Operator functions

Kotlin operators are really function calls by convention — a + b compiles to a.plus(b). Mark a function operator fun with the right name and your type gains that operator:

Vec.kt
data class Vec(val x: Int, val y: Int) {
    operator fun plus(other: Vec) = Vec(x + other.x, y + other.y)
    operator fun times(scale: Int) = Vec(x * scale, y * scale)
}

val a = Vec(1, 2)
val b = Vec(3, 4)
println(a + b)     // Vec(x=4, y=6)  — calls plus()
println(a * 3)     // Vec(x=3, y=6)  — calls times()
OperatorFunction name
a + ba.plus(b)
a - ba.minus(b)
a * ba.times(b)
a[i]a.get(i)
a in bb.contains(a)
a..ba.rangeTo(b)

get & set

The get and set conventions give your type indexing syntax with square brackets — and they can take multiple indices, which is perfect for grids and matrices:

Grid.kt
class Grid(val cols: Int) {
    private val data = HashMap<Int, Int>()
    operator fun get(r: Int, c: Int) = data[r * cols + c] ?: 0
    operator fun set(r: Int, c: Int, value: Int) {
        data[r * cols + c] = value
    }
}

val grid = Grid(2)
grid[0, 1] = 5          // calls set(0, 1, 5)
println(grid[0, 1])     // calls get(0, 1) -> 5

Note

Other handy conventions: invoke lets you call an object like a function (myObject()), compareTo enables </>, and iterator makes your type work in a for loop. Kotlin's collections use all of these.

infix functions

An infix function (one parameter) can be called without the dot and parentheses, reading like a keyword. You've used one all along — to, which builds a Pair:

Infix.kt
// an infix function can be called without dots or parentheses
infix fun String.repeatedTimes(n: Int) = this.repeat(n)

println("ab" repeatedTimes 3)   // ababab

// the built-in 'to' is infix — it's how you write map pairs
val pair = "Sara" to 25
println(pair)                   // (Sara, 25)

Don't overdo it

Operator overloading and infix functions are powerful but easy to abuse. Overload an operator only when the meaning is obvious (adding two vectors is clear; "adding" two users is not). When in doubt, a well-named regular method is clearer.

Recap & quick check

Key takeaways

  • Operators are functions by convention: a + b calls a.plus(b).
  • Mark a function 'operator fun' with the convention name to overload an operator.
  • get/set conventions give indexing syntax (a[i]); they can take multiple indices.
  • infix functions (one parameter) read like keywords: '3 repeatedTimes 2', or the built-in 'to'.
  • Overload only when the meaning is obvious; otherwise a named method is clearer.

Quick check

1. What does 'a + b' compile to?

2. How do you overload an operator?

3. Which conventions enable a[i] indexing syntax?

4. What does an infix function let you do?

5. When should you overload an operator?

Great — your types can read like built-ins now. Next up: Module 25 — Destructuring, Type Aliases & Idioms.