最佳实践
让运算符符合直觉
最佳实践 是 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 反馈 — 无需本地设置。