Kotlin Operator Overloading
Kotlin allows operator overloading, meaning you can define how operators like +, -, *, and others behave when used with your user-defined types (i.e., custom classes). This makes working with objects more intuitive and expressive. To overload an operator, you define a function (either a member or extension function) and mark it with the operator modifier. Kotlin provides a specific set of function names that correspond to standard operators.
Kotlin maps operator symbols (like +, -, etc.) to specific function names (like plus(), minus()). These functions can be implemented inside your class or as extensions. The function must be marked with the operator keyword to be used as an overloaded operator.
1. Unary Operators
The following table shows the various functions that can be defined for unary operators. These functions modify the calling instance.
Operator expression | Corresponding function |
---|---|
+x | x.unaryPlus() |
-x | x.unaryMinus() |
!x | x.not() |
Here, x corresponds to the type for which the operator is defined. The overloaded functionality is defined within the respective functions.
Example:
class MyString(val text: String) {
// overloading the function
operator fun unaryMinus(): MyString {
return MyString(text.reversed())
}
override fun toString() = text
}
fun main() {
val str = MyString("HELLO")
println("Initial string is ${str}")
//calling the overloaded function unaryMinus()
val reversed = -str
println("String after applying unary operator $reversed")
}
Output:
Initial string is HELLO
String after applying unary operator OLLEH
2. Increment and Decrement Operators
The increment and decrement operator can be defined for a type through the following functions. These function returns a new instance with the outcome of the expression.
Operator expression | Corresponding function |
---|---|
++x | x.inc() |
- - x | x.dec() |
Either used in postfix or prefix notation these functions work well in both the cases, with the same expected output, as one would expect when using prefix or postfix notations.
Example:
class CustomString(var value: String) {
// overloading increment function
operator fun inc(): CustomString {
value += "a"
return this
}
override fun toString() = value
}
fun main() {
var str = CustomString("Hello")
println(str)
str++
println(str)
}
Output:
Hello
Helloa
3. Binary Operators
The following table shows the binary operators and their equivalent functions to be defined. All these functions modify the calling instance.
Operator expression | Corresponding function |
---|---|
x1 + x2 | x1.plus(x2) |
x1 - x2 | x1.minus(x2) |
x1 * x2 | x1.times(x2) |
x1/ x2 | x1.div(x2) |
x1 % x2 | x1.rem(x2) |
x1..x2 | x1.rangeTo(x2) |
Example:
class CustomData(val name: String, val value: Int) {
// Overloading the function
operator fun plus(other: CustomData): CustomData {
return CustomData("$name & ${other.name}", this.value + other.value)
}
override fun toString() = "Name is $name and data is $value"
}
fun main() {
val a = CustomData("Chair", 4)
val b = CustomData("Table", 5)
// Calling the overloaded function
val result = a + b
println(result)
}
Output:
Name is Chair & Table and data is 9
4. Relational Operator
Kotlin doesn't provide direct operator functions for relational operators (<, >, <=, >=). Instead, you must implement the Comparable<T> interface and override the compareTo function.
Example:
class Score(val value: Int) : Comparable<Score> {
override fun compareTo(other: Score): Int {
return this.value - other.value
}
}
fun main() {
val s1 = Score(10)
val s2 = Score(15)
println(s1 < s2)
}
Output:
true
Other Operators
Kotlin supports a wide range of operators, hence defining each for a type is not a good programming practice. The following table shows some of the other useful operators that can be overloaded is Kotlin.
Operator expression | Corresponding function |
---|---|
x1 in x2 | x2.contains(x1) |
x1 !in x2 | !x2.contains(x1) |
x[i] | x.get(i) |
x[i, j] | x.get(i, j) |
x[i] = b | x.set(i, b) |
x[i, j] = b | x.set(i, j, b) |
x() | x.invoke() |
x(i) | x.invoke(i) |
x(i, j) | x.invoke(i, j) |
x1 += x2 | x1.plusAssign(x2) |
x1 -= x2 | x1.minusAssign(x2) |
x1 *= x2 | x1.timesAssign(x2) |
x1 /= x2 | x1.divAssign(x2) |
x1 %= x2 | x1.remAssign(x2) |