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*withoperator fun - Use the
get/setconventions for indexing - Write
infixfunctions 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:
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()| Operator | Function name |
|---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a[i] | a.get(i) |
a in b | b.contains(a) |
a..b | a.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:
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) -> 5Note
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:
// 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
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.