最近在 portal.kotlin-academy.com/#/ 上看到很多关于 Kotlin 的有趣的题目。个人觉得很适合 Kotlin 爱好者,感兴趣的小伙伴可自行查阅。
【有趣的 Kotlin 】系列记录自己对每一题的理解。
0x06:List minus list
fun main(args: Array<String>) {
val list = listOf(1, 2, 3)
print(list - 1)
print(list - listOf(1))
val ones = listOf(1, 1, 1)
print(ones - 1)
print(ones - listOf(1))
}
以上代码,运行结果是什么?可选项:
思考一下,记录下你心中的答案。
分析
题目如上,熟悉 Kotlin 集合相关操作符逻辑的朋友应该能很快能得到答案,这里只涉及 - 一个运算符和两个重载实现。
第一个重载实现:
逻辑就是过滤掉集合中第一个与传入参数 element 相等的元素。
第二个重载实现:
逻辑就是过滤掉与第二个列表中的任意元素相等的所有元素。
题中代码等价于如下写法:
fun main(args: Array<String>) {
val list = listOf(1, 2, 3)
print(list.minus(1))
print(list.minus(listOf(1)))
val ones = listOf(1, 1, 1)
print(ones.minus(1))
print(ones.minus(listOf(1)))
}
逐个查看运算逻辑
val list = listOf(1, 2, 3)
print(list - 1) // 过滤掉第一个等于 1 的元素,结果为 [2, 3]
print(list - listOf(1)) // 过滤掉所有等于 1 的元素,结果为 [2, 3]
val ones = listOf(1, 1, 1)
print(ones - 1) // 过滤掉第一个等于 1 的元素,结果为 [2, 3]
print(ones - listOf(1)) // 过滤掉所有等于 1 的元素,结果为 []
所以,正确答案为
选项 2 :[2, 3][2, 3][1, 1][]
延伸
Kotlin 中使用 operator 关键字用于修饰函数,表示该函数重载一个操作符或者实现一个约定。使用 operator 关键字修饰函数并且函数名只能为component1、component2、component3 … 时则是实现一个约定,即 解构 。
顺便看看 List 的一些其他操作符(in 、+)以及 解构约定。
fun main() {
val list = listOf(1, 2, 3)
println(2 in list)
println(4 in list)
println(list + 5)
println(list + listOf(1, 2, 3))
println(list + arrayOf(1, 2, 3))
println(list + sequenceOf(1, 2, 3))
val (v, w, x) = list
println(v)
println(w)
println(x)
println(list.component1())
println(list.component2())
println(list.component3())
println(list.component4())
println(list.component5())
}
运行结果如下:
componentN 最多支持到 5, 且 receiver 为 List,内部其实调用的就是 get() 函数,所以当索引超出列表长度时,运行时报错。
针对 operator 和 componentN 的配合方式,举例说明一下:
class Location(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
fun main() {
val location = Location(520, 1314)
val (x, y) = location
println(x)
println(y)
}
运行结果:
针对操作符重载,也举例说明下:
operator fun Location.minus(location: Location): Location {
return Location(this.x - location.x, this.y - location.y)
}
operator fun Location.plus(location: Location): Location {
return Location(this.x + location.x, this.y + location.y)
}
operator fun Location.contains(location: Location): Boolean {
return this.x > location.x && this.y > location.y
}
class Location(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
override operator fun equals(other: Any?): Boolean =
other is Location && this.x == other.x && this.y == other.y
override fun hashCode(): Int {
var result = x
result = 31 * result + y
return result
}
override fun toString(): String {
return "($x, $y)"
}
}
fun main() {
val location = Location(520, 1314)
val (x, y) = location
println(x)
println(y)
val other = Location(1, 2)
println(location - other)
println(location + other)
println(location == other)
println(other in location)
}
运行结果如下:
注意一下:
equals操作符重载只能作为类成员函数实现,因为此函数已在Any类中定义。
编写过程中, Android Studio 会给予我们友好的提示,不愧是 YYDS 。
总结
- 操作符重载
- 解构