Operator Overloading
| Since: | Kotlin 1.0(2016) |
|---|
In Kotlin, you can redefine operators (+, -, *, [], (), etc.) for your own classes using the operator keyword. Operator overloading lets you work with objects using intuitive notation.
Syntax
data class Point(val x: Int, val y: Int) {
// Overload the + operator
operator fun plus(other: Point): Point = Point(x + other.x, y + other.y)
// Overload the - operator
operator fun minus(other: Point): Point = Point(x - other.x, y - other.y)
// Unary minus
operator fun unaryMinus(): Point = Point(-x, -y)
}
Operator list
| Operator | Function name | Description |
|---|---|---|
| +a | unaryPlus() | Unary plus. |
| -a | unaryMinus() | Unary minus. |
| a + b | plus(b) | Addition. |
| a - b | minus(b) | Subtraction. |
| a * b | times(b) | Multiplication. |
| a / b | div(b) | Division. |
| a % b | rem(b) | Remainder. |
| a[i] | get(i) | Index access. |
| a[i] = v | set(i, v) | Index assignment. |
| a() | invoke() | Function call. |
| a in b | contains(a) | Containment check. |
| a..b | rangeTo(b) | Range creation. |
Sample code
sample_operator_overloading.kt
data class Vector(val x: Double, val y: Double) {
operator fun plus(other: Vector) = Vector(x + other.x, y + other.y)
operator fun minus(other: Vector) = Vector(x - other.x, y - other.y)
operator fun times(scale: Double) = Vector(x * scale, y * scale)
operator fun unaryMinus() = Vector(-x, -y)
// Overloading invoke() lets you call the object like a function (returns magnitude)
operator fun invoke(): Double = Math.sqrt(x * x + y * y)
override fun toString() = "($x, $y)"
}
// Advanced use case
class Matrix2x2(val a: Double, val b: Double, val c: Double, val d: Double) {
// Access elements with [] (row and col are zero-based)
operator fun get(row: Int, col: Int): Double = when {
row == 0 && col == 0 -> a
row == 0 && col == 1 -> b
row == 1 && col == 0 -> c
else -> d
}
}
fun main() {
val v1 = Vector(3.0, 4.0)
val v2 = Vector(1.0, 2.0)
println(v1 + v2) // (4.0, 6.0)
println(v1 - v2) // (2.0, 2.0)
println(v1 * 2.0) // (6.0, 8.0)
println(-v1) // (-3.0, -4.0)
println(v1()) // 5.0 (magnitude: √(9+16))
val mat = Matrix2x2(1.0, 2.0, 3.0, 4.0)
println(mat[0, 0]) // 1.0
println(mat[1, 1]) // 4.0
}
The command looks like this:
kotlinc sample_operator_overloading.kt -include-runtime -d sample_operator_overloading.jar java -jar sample_operator_overloading.jar (4.0, 6.0) (2.0, 2.0) (6.0, 8.0) (-3.0, -4.0) 5.0 1.0 4.0
Common Mistakes
// forgetting the operator keyword
data class Score(val value: Int) {
fun plus(other: Score): Score = Score(value + other.value) // missing operator
}
fun main() {
val s1 = Score(120)
val s2 = Score(90)
// val s3 = s1 + s2 // Compile error: unresolved reference: plus
val s3 = s1.plus(s2) // Regular function call still works
println(s3) // Score(value=210)
// defining times(Double) and then trying to multiply by Int
// val v = Vector(1.0, 2.0) * 3 // Compile error: operator fun times(scale: Double), Int not accepted
}
Common mistake 2 is not implementing Comparable when trying to use > or <. The correct approach is shown below.
sample_operator_overloading_ok.kt
// Definition with operator keyword
data class DeathNotes(val count: Int) {
operator fun plus(other: DeathNotes): DeathNotes = DeathNotes(count + other.count)
}
// Implementing Comparable enables comparison operators
data class Intelligence(val iq: Int) : Comparable<Intelligence> {
override fun compareTo(other: Intelligence): Int = iq - other.iq
}
fun main() {
val d1 = DeathNotes(1)
val d2 = DeathNotes(2)
println(d1 + d2) // DeathNotes(count=3)
val light = Intelligence(167)
val l_lawliet = Intelligence(212)
println(light < l_lawliet) // true
}
The command looks like this:
kotlinc sample_operator_overloading_ok.kt -include-runtime -d sample_operator_overloading_ok.jar java -jar sample_operator_overloading_ok.jar DeathNotes(count=3) true
Notes
Operator overloading is especially powerful for classes that represent mathematical concepts such as numeric calculations, coordinates, matrices, and complex numbers. Using operators in non-obvious ways can lead to confusion, so overloading is commonly applied when the meaning is immediately clear from the operator's mathematical or domain-specific intent.
Overloading invoke() lets you call an object as if it were a function. This is useful in DSLs and builder patterns.
For data class features, see data class. For class inheritance, see open / override.
If you find any errors or copyright issues, please contact us.