In Kotlin, type operators such as plus arithmetic operator (+
) and minus arithmetic operator (-
) call a corresponding function member to perform the intended operations.
For example, to calculate Int
types of a + b
Kotlin will call .plus()
function underneath:
val a: Int = 5
val b: Int = 2
print(a + b) // a.plus(b) = 7
But notice how when you call the same +
operator on String
types, the operation changes from calculating the sum of two numbers into concatenating the letters:
val a: String = "Hello"
val b: String = " World"
print(a + b) // a.plus(b) = Hello World
This is possible because operator functions like .plus()
are overloaded in Kotlin.
In the String
class, the plus()
function definition is as shown below:
public operator fun plus(other: Any?): String
But for Int
class, the definition is like this:
public operator fun plus(other: Int): Int
This is what operator overloading is in Kotlin. It allows you to overload the member functions that are called by type operators.
With operator overloading, you can define what Kotlin will do when operators are called on your class
or data class
instances.
For example, suppose you have a User
class with one property initialized from the constructor called name
:
class User(val name: String)
Let’s create an object of User
type and call the +
operator to perform concatenation and see what happens:
val user = User("Nathan")
print(user + "Mr. ")
When you run the above code, Kotlin will throw an Unresolved reference error
because the receiver type User
is not known to Kotlin.
To resolve this error, you need to overload the plus()
function.
To overload an operator function, you need to add the operator
modifier before the fun
keyword as shown below:
class User(val name: String) {
operator fun plus(other: String): String {
return this.name + other
}
}
val user = User("Nathan")
print(user + " Sebhastian") // user.plus(" Sebhastian") = Nathan Sebhastian
With the plus()
function being overloaded in the User
class, the +
operator now knows what to do when user.plus()
is called.
Why operator overloading is useful
Operator overloading is useful because it allows Kotlin operators to have meanings for classes and types that you define in your source code.
Instead of just throwing an error because it doesn’t know what to do with your data type, operator overloading allows you to integrate custom-defined types as if they were built-in types.
Another example where operator overloading comes in handy is when you have a custom data type involving geometric shapes.
Suppose you have a Rectangle
shape with length
and width
properties as shown below:
data class Rectangle(length: Int, width: Int)
You can override the unary minus operator -
to transform both length
and width
properties to their equivalent negative values.
Add the unaryMinus()
function to your data class
definition as shown below:
data class Rectangle(val length: Int, val width: Int) {
operator fun unaryMinus(): Rectangle {
return Rectangle(-this.length, -this.width)
}
}
Note that unaryMinus()
is the function called when you place the minus operator before the variable name.
If you place the minus operator after the variable name, you need to overload the minus()
function.
Here’s an example of calling unaryMinus()
from Rectangle
class:
val rec = Rectangle(8, 5)
val recMinus = -rec
println(recMinus.length) // -8
println(recMinus.width) // -5
Without operator overloading, you have to manually re-assign the property values.
Kotlin only allows you to overload a pre-defined set of operators. You can refer to Kotlin operator overloading documentation for a full list of operators that you can overload with their specific rules.
I hope this tutorial has helped you learn how operator overloading works in Kotlin. 🙏