Skip to content

操作符

源:Operator overloading

Kotlin 语言设计中有一个核心概念叫 “约定 (Conventions)”。与 C++ 或 Scala 允许定义任意符号的操作符不同,Kotlin 采用了一种更克制的方式:语言保留特定的符号(如 +, *, []),但允许你通过定义特定名称的函数(如 plus, times, get )来重载这些符号的行为。

只要函数标记为 operator 且名称匹配,编译器就会自动应用这些约定。

算术与比较操作符

这是最基础的重载,允许自定义类型参与数学运算。

符号对应的 operator 函数翻译逻辑
a + bplusa.plus(b)
a - bminusa.minus(b)
a * btimesa.times(b)
a / b`div"a.div(b)
a % brema.rem(b)
a..brangeToa.rangeTo(b)
a in bcontainsb.contains(a) (注意主客体反转)
kotlin
data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point) = Point(x + other.x, y + other.y)
}

val p = Point(1, 1) + Point(2, 2) // Point(3, 3)

比较操作符

  • == 对应 equals(注意:equals 是 Any 的成员,重写时无需 operator,只需 override)。
  • > / < 对应 compareTo(需实现 Comparable 接口)。

索引访问操作符 (Indexed Access)

这允许对象像数组或 Map 一样使用方括号 [] 进行读写。

  • 读取 a[i] 对应 a.get(i)
  • 写入 a[i] = b 对应 a.set(i, b)
kotlin
class Matrix(val rows: Int, val cols: Int) {
    private val data = IntArray(rows * cols)

    operator fun get(r: Int, c: Int): Int = data[r * cols + c]
    operator fun set(r: Int, c: Int, v: Int) {
        data[r * cols + c] = v
    }
}

val m = Matrix(3, 3)
m[1, 1] = 5 // 调用 m.set(1, 1, 5)

Invoke 操作符

invoke 是一个非常特殊的约定,它允许对象像函数一样被直接调用

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

val hello = Greeter("Hello")
hello("World") // 等价于 hello.invoke("World")

应用场景

invoke 常用于 Gradle 脚本配置、拦截器链(Interceptor Chain)或将单一职责的对象伪装成函数。

解构声明 (Destructuring Declarations)

解构声明(如 val (x, y) = point)通过 componentN 约定实现。

  • val (a, b) = obj
  • 翻译为
    • val a = obj.component1()
    • val b = obj.component2()

data class 会自动生成这些函数。对于普通类,你可以手动定义:

kotlin
class User(val name: String, val age: Int) {
    operator fun component1() = name
    operator fun component2() = age
}

属性委托约定

属性委托 (by 关键字) 的底层也是基于操作符约定的。关于委托的详细机制与实战,请参阅 委托机制 章节。

这里仅列出其核心约定:

  • 读取 (val): 编译器查找 getValue 操作符。
  • 写入 (var): 编译器查找 setValue 操作符。
kotlin
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        ...
    }
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        ...
    }
}

中缀表达 (Infix Notation)

infix 关键字允许省略点和括号。虽然它不需要 operator 关键字,但它也是一种语法约定。

限制条件

  1. 必须是成员函数或扩展函数。
  2. 必须只有一个参数
  3. 不能有默认参数或 vararg
kotlin
infix fun String.vs(other: String) = "$this vs $other"

// 调用
"Team A" vs "Team B"

标准库中的中缀函数

  • to: 1 to "one" (生成 Pair)
  • until: 0 until 10 (生成 Range)
  • shr / shl: 位运算

总结

Kotlin 的约定机制非常强大,它在不引入新语法的前提下,让自定义类型拥有了原生类型般的“手感”。

  • 算术 -> plus, minus...
  • 数组感 -> get, set
  • 函数感 -> invoke
  • 多返回值 -> componentN (解构)
  • DSL 语义 -> infix