0Pricing
Kotlin Academy · 课时

最佳实践

让运算符符合直觉

最佳实践 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

让运算符直观易懂

只有在含义显而易见时才重载运算符。向量上的 + 很清楚;表示发送电子邮件的 + 则不合适。

data class Vec(val x: Int, val y: Int) {
    operator fun plus(o: Vec) = Vec(x + o.x, y + o.y)
}

fun main() {
    println(Vec(1, 2) + Vec(3, 4))
}

保留预期的语义

请遵循通常的代数规律:对于对称类型,a + b 应当具有交换性,不应让读者感到意外。

data class Money(val cents: Int) {
    operator fun plus(o: Money) = Money(cents + o.cents)
}

fun main() {
    println(Money(100) + Money(50))
}

优先使用不可变性

像 plus 这样的运算符应返回一个新值,而不是修改接收者,这与数字的行为一致。

data class Point(val x: Int) {
    operator fun plus(o: Point) = Point(x + o.x)
}

fun main() {
    val a = Point(1)
    val b = a + Point(2)
    println("${a.x} ${b.x}")
}

plus 与 plusAssign

对于不可变值,请定义 plus;对于可变容器的原地修改,请定义 plusAssign。避免同时定义二者却让它们的行为相互冲突。

class Cart {
    val items = mutableListOf<String>()
    operator fun plusAssign(item: String) { items.add(item) }
}

fun main() {
    val c = Cart()
    c += "book"
    println(c.items)
}

匹配返回类型

二元运算符应返回相同类型的值,使结果可以自然地链式调用。

data class N(val v: Int) {
    operator fun plus(o: N) = N(v + o.v)
}

fun main() {
    println((N(1) + N(2) + N(3)).v)
}

不要为了炫技而重载

如果使用命名函数读起来更清楚,就使用命名函数。运算符适用于真正具有数值或容器语义的操作,而不是猜谜。

data class Log(val lines: List<String>) {
    fun append(line: String) = Log(lines + line)
}

fun main() {
    println(Log(emptyList()).append("start").lines)
}

保持 equals 和 hashCode 一致

如果您重载了比较或相等判断,请保持 equals 和 hashCode 同步,以免破坏集合。

data class Id(val v: Int)

fun main() {
    val set = hashSetOf(Id(1), Id(1))
    println(set.size)
}

注意运算符优先级

重载运算符会保留内置优先级。请进行合理设计,使自然的优先级产生预期结果。

data class N(val v: Int) {
    operator fun plus(o: N) = N(v + o.v)
    operator fun times(o: N) = N(v * o.v)
}

fun main() {
    println((N(2) + N(3) * N(4)).v)
}

记录令人意外的运算符

如果运算符执行了任何不明显的操作,请添加注释或 KDoc,以免误导调用者。

data class Range2(val start: Int, val end: Int) {
    // contains: is value inside the range?
    operator fun contains(v: Int) = v in start..end
}

fun main() {
    println(5 in Range2(1, 10))
}

使用 contains 实现 in

in 运算符映射到 contains。只有当成员关系对您的类型有明确含义时,才重写它。

class Team(val members: List<String>) {
    operator fun contains(name: String) = name in members
}

fun main() {
    val t = Team(listOf("Ann", "Bo"))
    println("Ann" in t)
}

不确定时,请使用函数

清晰命名的方法几乎总是比晦涩的运算符更安全。只有在重载确实能提高可读性时,才使用重载。

data class Temperature(val c: Int) {
    fun warmerThan(o: Temperature) = c > o.c
}

fun main() {
    println(Temperature(30).warmerThan(Temperature(20)))
}

快速检查

请检验您对运算符重载最佳实践的理解。

回顾

您学会了运算符重载的最佳实践:

  • 只有在含义明显且符合惯例时才重载。
  • 优先使用不可变的同类型返回值;让结果可以链式调用。
  • 将 plus(值)与 plusAssign(修改)区分开。
  • 保持 equals/hashCode 一致;更清楚时优先使用命名函数。
data class V(val x: Int) {
    operator fun plus(o: V) = V(x + o.x)
}

fun main() {
    println((V(2) + V(5)).x)
}

常见问题解答

「最佳实践」课时是免费的吗?

是的 — 「最佳实践」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「最佳实践」这节课中我会学到什么?

让运算符符合直觉 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「最佳实践」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 运算符函数
  2. 比较运算符
  3. 索引与调用
  4. 最佳实践
← 返回 Kotlin Academy