ZetCode

Kotlin 运算符关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的运算符重载允许您为标准运算符定义自定义行为。operator 关键字标记重载运算符的函数。本教程通过示例深入探讨了运算符重载。

基本定义

Kotlin 中的 operator 关键字用于声明重载运算符的函数。每个运算符都有一个预定义的函数名,必须使用该名称。运算符重载使代码更具可读性,并且对特定于域的操作更直观。

重载加号运算符

plus 运算符函数重载 + 运算符。您可以为您的类定义自定义的加法行为。该函数必须使用 operator 关键字标记。

PlusOperator.kt
package com.zetcode

data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point): Point {
        return Point(x + other.x, y + other.y)
    }
}

fun main() {

    val p1 = Point(1, 2)
    val p2 = Point(3, 4)
    val p3 = p1 + p2
    
    println(p3) // Output: Point(x=4, y=6)
}

在这里,我们为 Point 类定义了一个 plus 运算符。它将两个点的 x 和 y 坐标相加。+ 运算符现在可以与 Point 对象一起使用,使代码更直观。

重载比较运算符

可以使用 compareTo 函数重载比较运算符,如 <>。此函数应返回一个负整数、零或正整数。

ComparisonOperator.kt
package com.zetcode

data class Person(val name: String, val age: Int) {
    operator fun compareTo(other: Person): Int {
        return age.compareTo(other.age)
    }
}

fun main() {

    val alice = Person("Alice", 30)
    val bob = Person("Bob", 25)
    
    println(alice > bob) // Output: true
    println(alice < bob) // Output: false
}

compareTo 运算符按年龄比较 Person 对象。这使得可以使用标准比较运算符与自定义对象。该函数返回比较年龄的结果。

重载索引访问运算符

getset 运算符重载索引访问运算符 []。这允许您的类表现得像一个数组或集合。

IndexOperator.kt
package com.zetcode

class StringCollection(private val strings: List) {
    operator fun get(index: Int): String {
        return strings[index]
    }
    
    operator fun set(index: Int, value: String) {
        // Strings are immutable in List, so this is just for demo
        println("Setting index $index to $value")
    }
}

fun main() {

    val collection = StringCollection(listOf("a", "b", "c"))
    println(collection[1]) // Output: b
    collection[1] = "x"   // Output: Setting index 1 to x
}

在这里,我们为 StringCollection 类定义了索引访问。get 运算符检索值,而 set 运算符允许修改。请注意,在本例中,底层 List 是不可变的。

重载调用运算符

invoke 运算符允许将对象像函数一样调用。这可以使 DSL 或构建器模式更简洁易读。

InvokeOperator.kt
package com.zetcode

class Greeter(private val greeting: String) {
    operator fun invoke(name: String) {
        println("$greeting, $name!")
    }
}

fun main() {

    val hello = Greeter("Hello")
    hello("John") // Output: Hello, John!
    
    val hi = Greeter("Hi")
    hi("Sarah")   // Output: Hi, Sarah!
}

Greeter 类定义了一个 invoke 运算符,该运算符接受一个名称并打印问候语。这允许像函数一样调用 Greeter 实例,使语法更自然地用于此用例。

重载一元运算符

可以使用特定的函数名称重载一元运算符,如 +-!。这些运算符对单个操作数起作用。

UnaryOperator.kt
package com.zetcode

data class Counter(var value: Int) {
    operator fun inc(): Counter {
        return Counter(value + 1)
    }
    
    operator fun dec(): Counter {
        return Counter(value - 1)
    }
    
    operator fun unaryMinus(): Counter {
        return Counter(-value)
    }
}

fun main() {

    var counter = Counter(5)
    println(++counter)  // Output: Counter(value=6)
    println(--counter)  // Output: Counter(value=5)
    println(-counter)   // Output: Counter(value=-5)
}

Counter 类重载了递增 (inc)、递减 (dec) 和一元减 (unaryMinus) 运算符。这使得可以使用 ++--- 与 Counter 对象。

重载复合赋值运算符

可以使用 plusAssign 函数重载复合赋值运算符,如 +=。这些运算符修改左操作数。

CompoundOperator.kt
package com.zetcode

class ShoppingCart {
    private val items = mutableListOf()
    
    operator fun plusAssign(item: String) {
        items.add(item)
    }
    
    fun showItems() {
        println("Cart contains: $items")
    }
}

fun main() {

    val cart = ShoppingCart()
    cart += "Apple"
    cart += "Banana"
    cart.showItems() // Output: Cart contains: [Apple, Banana]
}

ShoppingCart 类使用 plusAssign 重载了 += 运算符。这允许使用简洁的语法将项目添加到购物车。该运算符修改购物车的内部状态。

重载范围运算符

rangeTo 运算符重载 .. 运算符以创建范围。这对于创建特定于域的范围表达式很有用。

RangeOperator.kt
package com.zetcode

data class Date(val year: Int, val month: Int, val day: Int) {
    operator fun rangeTo(other: Date): List {
        val dates = mutableListOf()
        var current = this
        
        while (current <= other) {
            dates.add(current)
            current = current.nextDay()
        }
        
        return dates
    }
    
    private fun nextDay(): Date {
        // Simplified implementation
        return if (day < 28) copy(day = day + 1)
        else if (month < 12) copy(month = month + 1, day = 1)
        else copy(year = year + 1, month = 1, day = 1)
    }
    
    operator fun compareTo(other: Date): Int {
        return when {
            year != other.year -> year - other.year
            month != other.month -> month - other.month
            else -> day - other.day
        }
    }
}

fun main() {

    val start = Date(2023, 1, 1)
    val end = Date(2023, 1, 3)
    val range = start..end
    
    println(range) // Output: [Date(year=2023, month=1, day=1), ...]
}

此示例显示了如何使用 .. 运算符创建日期范围。rangeTo 运算符生成两个日期之间的所有日期。比较范围也需要 compareTo 运算符。

运算符重载的最佳实践

来源

Kotlin 运算符重载文档

本教程深入探讨了 Kotlin 的 operator 关键字,展示了如何为自定义类型重载各种运算符。我们探讨了算术、比较、索引和其他运算符类型。正确使用运算符重载可以使特定于域的代码更具表现力和可读性。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有多年的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1400 多篇文章和 8 本电子书。我拥有超过八年的编程教学经验。

列出 所有 Kotlin 教程