kotlinintermediate

Operator Overloading

Overload operators in Kotlin: arithmetic, comparison, indexing, invoke, and destructuring.

kotlin
data class Vector2D(val x: Double, val y: Double) {
    // Arithmetic operators
    operator fun plus(other: Vector2D) = Vector2D(x + other.x, y + other.y)
    operator fun minus(other: Vector2D) = Vector2D(x - other.x, y - other.y)
    operator fun times(scalar: Double) = Vector2D(x * scalar, y * scalar)
    operator fun div(scalar: Double) = Vector2D(x / scalar, y / scalar)
    operator fun unaryMinus() = Vector2D(-x, -y)

    // Comparison
    val magnitude get() = kotlin.math.sqrt(x * x + y * y)

    fun normalized(): Vector2D {
        val mag = magnitude
        return if (mag > 0) this / mag else this
    }

    override fun toString() = "(${"%.2f".format(x)}, ${"%.2f".format(y)})"
}

// Allow scalar * vector
operator fun Double.times(v: Vector2D) = v * this

// Matrix with indexing
class Matrix(val rows: Int, val cols: Int) {
    private val data = Array(rows) { DoubleArray(cols) }

    // Indexed access: matrix[i, j]
    operator fun get(row: Int, col: Int): Double = data[row][col]
    operator fun set(row: Int, col: Int, value: Double) { data[row][col] = value }

    // Matrix addition
    operator fun plus(other: Matrix): Matrix {
        require(rows == other.rows && cols == other.cols)
        val result = Matrix(rows, cols)
        for (i in 0 until rows)
            for (j in 0 until cols)
                result[i, j] = this[i, j] + other[i, j]
        return result
    }

    // Range check: value in matrix
    operator fun contains(value: Double): Boolean =
        data.any { row -> row.any { it == value } }

    override fun toString(): String =
        data.joinToString("\n") { row ->
            row.joinToString(", ") { "%.1f".format(it) }
        }
}

// Callable class with invoke
class Validator<T>(private val rules: List<(T) -> String?>) {
    operator fun invoke(value: T): List<String> =
        rules.mapNotNull { it(value) }
}

// Range operator
data class Version(val major: Int, val minor: Int, val patch: Int) : Comparable<Version> {
    override fun compareTo(other: Version): Int = when {
        major != other.major -> major - other.major
        minor != other.minor -> minor - other.minor
        else -> patch - other.patch
    }
    override fun toString() = "$major.$minor.$patch"
    operator fun rangeTo(other: Version) = VersionRange(this, other)
}

class VersionRange(val start: Version, val end: Version) {
    operator fun contains(version: Version) = version in start..end
}

fun main() {
    // Vector
    val a = Vector2D(1.0, 2.0)
    val b = Vector2D(3.0, 4.0)
    println("a + b = ${a + b}")
    println("a - b = ${a - b}")
    println("a * 3 = ${a * 3.0}")
    println("3 * a = ${3.0 * a}")
    println("-b = ${-b}")
    println("|b| = ${"%.2f".format(b.magnitude)}")

    // Matrix
    val m = Matrix(2, 2)
    m[0, 0] = 1.0; m[0, 1] = 2.0
    m[1, 0] = 3.0; m[1, 1] = 4.0
    println("\nMatrix:\n$m")
    println("Contains 3.0: ${3.0 in m}")

    // Validator with invoke
    val validateAge = Validator<Int>(listOf(
        { if (it < 0) "Age cannot be negative" else null },
        { if (it > 150) "Age seems unrealistic" else null }
    ))
    println("\nValidate 25: ${validateAge(25)}")
    println("Validate -1: ${validateAge(-1)}")

    // Version comparison
    val v1 = Version(1, 2, 0)
    val v2 = Version(2, 0, 0)
    println("\n$v1 < $v2: ${v1 < v2}")
}

Use Cases

  • Mathematical and scientific computing
  • Domain-specific operator semantics
  • Custom collection access patterns

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.