Hero image

Writing more concise code using Kotlin's operator overloading

Dec 19, 2023
Kotlin

Kotlin allows you to define custom implementations of operators such as +, *, !, <, etc. This can be used to improve the conciseness and readability of your code. Suppose you have a Vector class in a math library you’re creating being able to write vector1 + vector2 or 5 * vector is much more concise than vector1.add(vector2) or vector.scale(5).

To overload an operator, you need to define a member function or an extension function with a special name corresponding to the operator. For example, the + operator can be overloaded by defining a function named plus in our Vector class below.

data class Vector(val x: Int, val y: Int) {
    operator fun plus(other: Vector): Vector {
        return Vector(x + other.x, y + other.y)
    }
}

With the plus function defined, you can create two vectors and add them together.

val v1 = Vector(1, 2)
val v2 = Vector(10, 20)

println(v1 + v2) // prints "Vector(x=11, y=22)"

For a list of the operators you can overload and their corresponding function names, see operator overloading.

Operator Overloading with Extension Functions

It is also possible to overload an operator using extension functions in a similar way to defining a member function. Below overloads the * operator on Int allowing us to scale a vector using 5 * vector.

operator fun Int.times(vector: Vector) = Vector(this * vector.x, this * vector.y)

Now a Vector can be scaled by simply multiplying by an Int.

val v = Vector(2, 3)
println(5 * v) // prints "Vector(x=10, y=15)"

The order of the operands matters for example with the above extension function you won’t be able to multiply a Vector by an Int e.g. vector * 5. You’ll need to define an extension method that multiplies a Vector by an Int.

operator fun Vector.times(n: Int) = n * this

Possible Problems with Operator Overloading

Hopefully, the examples above have demonstrated how using operator overloading wisely can help improve the readability and conciseness of your code. However, overzealous use does have its pitfalls a big one is breaking conventions or diverting significantly from the typical meaning of an operator.

For example, using the unaryMinus operator - to reverse a String might seem sensible to you however it is likely to confuse most developers.

operator fun String.unaryMinus() = this.reversed()

println(-"Hello") // prints "olleH", but would likely confuse people :)