操作符重载(operator overloading)
⭐⭐⭐。
·
Kotlin基础知识点 #107: 操作符重载(operator overloading)
难度: ⭐⭐⭐
问题背景
在数学和日常编程中,我们习惯使用+、-、*等操作符。但当我们定义自己的类时,如何让这些操作符也能用于我们的类呢?例如,如何实现两个复数相加、两个向量相乘?
Kotlin的操作符重载功能允许我们为自定义类型定义这些操作符的行为,使代码更加直观和简洁。
核心概念
操作符重载允许你为预定义的操作符(如+、-、*、[]等)定义自定义行为。通过使用operator关键字修饰函数,并使用特定的函数名,就可以实现操作符重载。
语法格式:
operator fun 函数名(参数): 返回类型 {
// 实现
}
重要规则:
- 必须使用
operator关键字 - 必须使用Kotlin规定的特定函数名
- 可以是成员函数或扩展函数
- 参数个数必须符合操作符的要求
代码示例
示例1:算术操作符重载
// 定义一个复数类
data class Complex(val real: Double, val imaginary: Double) {
// 重载 + 操作符
operator fun plus(other: Complex): Complex {
return Complex(real + other.real, imaginary + other.imaginary)
}
// 重载 - 操作符
operator fun minus(other: Complex): Complex {
return Complex(real - other.real, imaginary - other.imaginary)
}
// 重载 * 操作符
operator fun times(other: Complex): Complex {
return Complex(
real * other.real - imaginary * other.imaginary,
real * other.imaginary + imaginary * other.real
)
}
// 重载一元 - 操作符
operator fun unaryMinus(): Complex {
return Complex(-real, -imaginary)
}
override fun toString() = "$real + ${imaginary}i"
}
// 使用
fun main() {
val c1 = Complex(3.0, 4.0)
val c2 = Complex(1.0, 2.0)
println(c1 + c2) // 4.0 + 6.0i
println(c1 - c2) // 2.0 + 2.0i
println(c1 * c2) // -5.0 + 10.0i
println(-c1) // -3.0 + -4.0i
}
示例2:比较操作符重载
// 定义一个版本号类
data class Version(val major: Int, val minor: Int, val patch: Int) : Comparable<Version> {
// 重载 > 操作符
operator fun compareTo(other: Version): Int {
if (major != other.major) return major - other.major
if (minor != other.minor) return minor - other.minor
return patch - other.patch
}
override fun toString() = "$major.$minor.$patch"
}
// 使用
fun main() {
val v1 = Version(1, 2, 3)
val v2 = Version(1, 3, 0)
val v3 = Version(2, 0, 0)
println(v1 < v2) // true
println(v2 > v1) // true
println(v3 >= v2) // true
}
示例3:索引访问操作符重载
// 定义一个矩阵类
class Matrix(private val rows: Int, private val cols: Int) {
private val data = Array(rows) { DoubleArray(cols) }
// 重载 [] 操作符(get)
operator fun get(row: Int, col: Int): Double {
return data[row][col]
}
// 重载 [] 操作符(set)
operator fun set(row: Int, col: Int, value: Double) {
data[row][col] = value
}
override fun toString(): String {
return data.joinToString("\n") { row ->
row.joinToString(" ") { "%.2f".format(it) }
}
}
}
// 使用
fun main() {
val matrix = Matrix(2, 2)
// 使用 [] 操作符赋值
matrix[0, 0] = 1.0
matrix[0, 1] = 2.0
matrix[1, 0] = 3.0
matrix[1, 1] = 4.0
// 使用 [] 操作符读取
println(matrix[0, 0]) // 1.0
println(matrix)
// 1.00 2.00
// 3.00 4.00
}
示例4:in操作符重载
// 定义一个日期范围类
data class DateRange(val start: Date, val end: Date) {
// 重载 in 操作符
operator fun contains(date: Date): Boolean {
return date >= start && date <= end
}
}
// 定义一个矩形类
data class Rectangle(val x: Int, val y: Int, val width: Int, val height: Int) {
// 重载 in 操作符,判断点是否在矩形内
operator fun contains(point: Point): Boolean {
return point.x in x..(x + width) && point.y in y..(y + height)
}
}
data class Point(val x: Int, val y: Int)
// 使用
fun main() {
val rect = Rectangle(0, 0, 100, 100)
val point1 = Point(50, 50)
val point2 = Point(150, 150)
println(point1 in rect) // true
println(point2 in rect) // false
}
示例5:invoke操作符重载
// 定义一个函数包装类
class Greeter(val greeting: String) {
// 重载 () 操作符
operator fun invoke(name: String) {
println("$greeting, $name!")
}
}
// 定义一个计算器类
class Calculator {
operator fun invoke(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
}
// 使用
fun main() {
val greeter = Greeter("Hello")
greeter("John") // Hello, John!
greeter("Jane") // Hello, Jane!
val calc = Calculator()
println(calc(10, 5, Int::plus)) // 15
println(calc(10, 5, Int::times)) // 50
}
示例6:增强赋值操作符
// 定义一个可变列表包装类
class MyList<T> {
private val items = mutableListOf<T>()
// 重载 += 操作符
operator fun plusAssign(item: T) {
items.add(item)
}
// 重载 -= 操作符
operator fun minusAssign(item: T) {
items.remove(item)
}
override fun toString() = items.toString()
}
// 使用
fun main() {
val list = MyList<Int>()
list += 1
list += 2
list += 3
println(list) // [1, 2, 3]
list -= 2
println(list) // [1, 3]
}
Kotlin操作符对应的函数名
算术操作符
| 操作符 | 函数名 | 说明 |
|---|---|---|
| a + b | plus | 加法 |
| a - b | minus | 减法 |
| a * b | times | 乘法 |
| a / b | div | 除法 |
| a % b | rem | 取余 |
| a…b | rangeTo | 范围 |
一元操作符
| 操作符 | 函数名 | 说明 |
|---|---|---|
| +a | unaryPlus | 一元加 |
| -a | unaryMinus | 一元减 |
| !a | not | 逻辑非 |
| ++a, a++ | inc | 自增 |
| –a, a– | dec | 自减 |
比较操作符
| 操作符 | 函数名 | 说明 |
|---|---|---|
| a > b | compareTo | 大于 |
| a < b | compareTo | 小于 |
| a >= b | compareTo | 大于等于 |
| a <= b | compareTo | 小于等于 |
索引访问操作符
| 操作符 | 函数名 | 说明 |
|---|---|---|
| a[i] | get | 读取索引 |
| a[i] = b | set | 设置索引 |
| a in b | contains | 包含 |
调用操作符
| 操作符 | 函数名 | 说明 |
|---|---|---|
| a() | invoke | 函数调用 |
增强赋值操作符
| 操作符 | 函数名 | 说明 |
|---|---|---|
| a += b | plusAssign | 加并赋值 |
| a -= b | minusAssign | 减并赋值 |
| a *= b | timesAssign | 乘并赋值 |
| a /= b | divAssign | 除并赋值 |
| a %= b | remAssign | 取余并赋值 |
关键要点
-
优先级和结合性:重载的操作符保持原有的优先级和结合性
-
equals和hashCode:
==操作符会自动调用equals()方法,不需要重载 -
plus vs plusAssign:
plus返回新对象:val c = a + bplusAssign修改原对象:a += b
-
可空性:操作符重载也支持可空类型
-
扩展函数:可以通过扩展函数为已有类添加操作符重载
operator fun String.times(n: Int): String = this.repeat(n) println("Hello" * 3) // HelloHelloHello
相关知识点
- 中缀函数(#106):另一种自定义操作符的方式
- 扩展函数(#104):可以用扩展函数实现操作符重载
- data class(#108):自动生成一些操作符函数
- 委托属性:
getValue和setValue也是操作符
最佳实践
- 只在操作符的语义明确且符合直觉时使用重载
- 保持操作符的数学和逻辑意义
- 不要过度使用,避免让代码难以理解
- 为自定义类型实现完整的操作符集合
- 考虑实现
equals()、hashCode()和toString()方法
更多推荐


所有评论(0)