操作符重载与解构
源:Operator overloading | Destructuring
Kotlin 允许我们为预定义的运算符(如 +, *, [], ())提供自定义实现。这一特性是构建 DSL (领域特定语言) 和优雅 API 的关键。
操作符重载 (Operator Overloading)
要重载操作符,必须使用 operator 修饰符标记函数。
算术与比较
所有算术符号都映射到特定的函数名。
kotlin
data class Point(val x: Int, val y: Int) {
// 重载 '+' 运算符
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
// 重载 'unaryMinus' (-p)
operator fun unaryMinus() = Point(-x, -y)
}
val p1 = Point(10, 20)
val p2 = Point(5, 5)
val p3 = p1 + p2 // 等价于 p1.plus(p2) -> Point(15, 25)索引访问 (Indexed Access)
通过重载 get 和 set,可以让对象像数组或 Map 一样通过 [] 访问。
kotlin
class Grid(val rows: Int, val cols: Int) {
private val matrix = Array(rows * cols) { 0 }
// obj[i, j]
operator fun get(row: Int, col: Int): Int {
return matrix[row * cols + col]
}
// obj[i, j] = value
operator fun set(row: Int, col: Int, value: Int) {
matrix[row * cols + col] = value
}
}
val grid = Grid(2, 2)
grid[0, 1] = 99 // 调用 set(0, 1, 99)
val cell = grid[0, 1] // 调用 get(0, 1)调用操作符 (Invoke)
重载 invoke 允许对象像函数一样被直接调用。这在 Gradle 脚本或拦截器链设计中非常常见。
kotlin
class Greeter(val name: String) {
operator fun invoke(msg: String) {
println("$name says: $msg")
}
}
val greet = Greeter("Jarvis")
greet("System online") // 等价于 greet.invoke(...)解构声明进阶 (Destructuring)
解构允许我们将一个对象拆解成多个变量。除了数据类 (Data Class) 自带解构外,我们也可以手动实现。
自定义解构
只需定义 componentN 操作符函数。
kotlin
class Location(val lat: Double, val lng: Double) {
operator fun component1() = lat
operator fun component2() = lng
}
val loc = Location(30.0, 120.0)
val (lat, lng) = loc // lat=30.0, lng=120.0循环与 Map 中的解构
这是解构最实用的场景之一。
kotlin
val map = mapOf("key1" to 1, "key2" to 2)
// Map.Entry 扩展了 component1() 和 component2()
for ((key, value) in map) {
println("$key -> $value")
}下划线 (Underscore)
如果不需要某个值,可以用 _ 占位,避免编译器警告未使用的变量。
kotlin
val (_, status) = getResult() // 只关心 status,忽略第一个返回值类型别名 (Type Aliases)
类型别名不会引入新类型,只是为现有类型取个“绰号”。它在简化泛型复杂度和增强代码语义上非常有用。
kotlin
// 1. 缩短冗长的泛型类型
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
// 2. 为函数类型赋予语义
typealias ClickHandler = (View, Int) -> Unit
fun setHandler(handler: ClickHandler) { /*...*/ }Inline Classes vs Type Aliases
- Type Alias: 编译时不产生新类型,完全等价于原类型(零开销,但无类型安全隔离)。
- Value Class (Inline): 包装了一个值,编译时尽可能优化,但在类型系统上是独立的(类型安全)。