Kotlin 運算子重載 (Operator Overloading)

Kotlin 允許我們為自定義的類別提供標準運算子 (如 +, -, *, /, == 等) 的實作。 這讓我們寫出來的程式碼更直觀、更像數學公式,而不只是一堆函式呼叫。

基本概念 (Syntactic Sugar)

在 Kotlin 中,運算子其實只是 特定名稱函式 的語法糖。 例如,當你寫 a + b 時,編譯器其實是幫你呼叫 a.plus(b)

要啟用這個功能,我們必須在函式前面加上 operator 關鍵字。

範例:向量加法

假設我們有一個 Point 類別,我們希望可以直接用 p1 + p2 來相加。

data class Point(val x: Int, val y: Int) {
    // 定義 + 運算子 (對應 plus 函式)
    operator fun plus(other: Point): Point {
        return Point(x + other.x, y + other.y)
    }
}

fun main() {
    val p1 = Point(10, 20)
    val p2 = Point(30, 40)
    
    val p3 = p1 + p2 // 自動呼叫 p1.plus(p2)
    println(p3)      // Point(x=40, y=60)
}

常見運算子對照表

算術運算子 (Arithmetic)

運算子對應函式名
a + bplus
a - bminus
a * btimes
a / bdiv
a % brem

索引存取運算子 (Indexed Access)

這是讓你可以用 [] 來存取物件的關鍵!(像 List 和 Map 那樣)

運算子對應函式名
a[i]get(i)
a[i] = bset(i, b)
class Box(val items: MutableList<String>) {
    operator fun get(index: Int): String {
        return items[index]
    }
    
    operator fun set(index: Int, value: String) {
        items[index] = value
    }
}

val box = Box(mutableListOf("A", "B"))
println(box[0]) // 呼叫 box.get(0)
box[1] = "C"    // 呼叫 box.set(1, "C")

呼叫運算子 (Invoke)

讓物件可以像函式一樣被呼叫 obj()

運算子對應函式名
a()invoke()
a(i)invoke(i)
class Greeter(val name: String) {
    operator fun invoke(msg: String) {
        println("$name says: $msg")
    }
}

val greet = Greeter("Miko")
greet("Hello!") // 等同於 greet.invoke("Hello!"),輸出: Miko says: Hello!

比較運算子 (Comparison)

運算子對應函式名
a > bcompareTo > 0
a < bcompareTo < 0
a == bequals (注意:equals 雖然也是 operator,但它是 Any 的成員,覆寫時只需 override 不需要加 operator 關鍵字)

總結

  1. operator 關鍵字:必須加才能重載。
  2. 固定名稱:不能隨便取名,必須對應標準的運算子名稱 (如 plus, minus, get)。
  3. 適度使用:不要濫用!只在語意清楚時使用 (例如向量相加),不要讓 + 變成「儲存到資料庫」這種奇怪的邏輯。