当我们在处理可以添加、删除、比较或者连接的类型时,我们通常需要写很多冗长和重复的代码。但在 Kotlin 中,我们可以借助 操作符重载,为这些类型写出更具表现力和简洁的代码。
我除了喜欢 Android,还喜欢在合唱团里唱歌,所以就让我们用合唱团的例子来说明操作符重载的好处。假设有一个由歌手组成的合唱团,我们想在合唱团中增加一名歌手,代码如下:
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class Choir {
private val singers = mutableListOf<Singer>()
fun addSinger(singer: Singer) {
singers.add(singer)
}
...
}
我们将添加一名新歌手,示例如下:
choir.addSinger(singer)
但是比起这种操作,使用 += 来操作会更适合一些,而且这样调用也更自然。
choir += singer
接着往下读,您会知道:
通过操作符重载,可以实现任意类型的一系列预定义操作符。操作符可以通过成员函数或者使用相应的成员函数的扩展函数来重载。比如: + 操作符可以通过 plus() 函数进行重载,+= 操作符可以通过 plusAssign() 函数进行重载。注意,操作符之间不会相互影响: 如果您重载了 +,是不会影响到 ++。
要重载一个操作符,您需要在 fun 的前面添加 operator 关键字,然后指定您想重载的操作符。如果您不添加 operator 关键字,编译器会把它当作一个普通的 Kotlin 函数来处理,甚至不会进行编译!
以下是 Kotlin 中可以重载的操作符:
△ 有关可以重载的操作符及其相应函数的完整列表,请参见相关文档
好了,开始吧,我们怎么才能在 Kotlin 中实现操作符的重载?
让我们使用初始示例中的 choir 类,我们需要重载 += 操作符来添加一名歌手。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class Choir {
private val singers = mutableListOf<Singer>()
operator fun plusAssign(singer: Singer) {
singers.add(singer)
}
}
您可以这样使用操作符:
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
data class Singer(val name: String)
fun main() {
val choir = Choir()
val singerMeghan = Singer("Meghan")
choir += singerMeghan
}
重载的操作符可以使代码更加的简洁和易读。
通常情况下您需要的操作符不止一个,但是重载一个自定义类型的所有操作符可能是没有任何意义的。过度的使用操作符重载会导致代码的可读性变差。所以需要多花点时间思考,对哪些操作符进行重载,可以提升代码的可读性。
我们重载 += 操作符是为了将某人加入合唱团,但我们可能也想看看这个人是否已经是合唱团的成员。要实现这一点,我们需要重载 contains 函数,这样我们就可以使用 in 操作符。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
operator fun contains(s: Singer) : Boolean {
return singers.contains(s)
}
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
data class Singer(val name: String)
fun main() {
val choir = Choir()
val singerMeghan = Singer("Meghan")
choir += singerMeghan
if(singerMeghan in choir){
println("Meghan is a part of the choir!")
}
}
也可以通过扩展函数来使用操作符重载。在这个示例中,我们重载了 ViewGroup 的 += 操作符:
operator fun ViewGroup.plusAssign(other: View) = addView(other)
现在给 viewGroup 添加一个 view 是如此的简单!
viewGroup += view
操作符重载也在许多其他编程语言中使用,比如: C++、Python、Swift 和 PHP。虽然我们在 Kotlin 中暂时还没有明确的最佳实践,但我们可以从这些语言中学习一些:
操作符重载是通过重写操作符的标准函数调用实现的,比如,添加合唱团成员的代码:
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
val choir = Choir()
val singerMeghan = Singer("Meghan")
choir += singerMeghan
如果我们看一下反编译的 Java 代码,可以看到它是如何工作的:
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
Choir choir = new Choir();
Singer singerMeghan = new Singer("Meghan");
choir.plusAssign(singerMeghan);
编译器只是简单的通过正常的成员函数调用替换了 +=。
操作符重载必须谨慎使用,但是如果您使用得当,它是一个可以使代码更具表现力和更加简洁的强大工具。