Writing more concise code using Kotlin's operator overloading
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 :)