阅读 323

Kotlin进阶知识(三)——集合与区间的约定

一、通过下标来访问元素:“get”和“set”

下标运算符:通过下标获取或设置元素,可以使用语法a[b]

在Kotlin中,可以用类似Java中数组的方式来访问map中的元素:

  • 使用方括号:
val value = map[key]
复制代码
  • 使用同样的运算符来改变一个可变map的元素:
mutableMap[key] = newValue
复制代码

使用下标运算符读取元素会被转换get运算符方法的调用,并且写入元素将调用set

  • 实现get约定
// 定义一个名为“get”的运算符函数
operator fun Point.get(index: Int): Int {
    return when(index) {
        // 根据给出的index返回对应的坐标
        0 -> x
        1 -> y
        else ->
            throw IndexOutOfBoundsException("Invalid coordinate $index")
    }
}

// 测试
>>> val p = Point(10, 20)
>>> println(p[1])
20
复制代码
  • 实现set约定
data class MutablePoint(var x: Int, var y: Int)

// 定义一个名为“set”的运算符函数
operator fun MutablePoint.set(index: Int, value: Int) {
    when(index) {
        // 根据给出的index修改对应的坐标
        0 -> x = value
        1 -> y = value
        else ->
            throw IndexOutOfBoundsException("Invalid coordinate $index")
    }
}

// 测试
>>> val p = MutablePoint(10, 20)
>>> p[1] = 42
>>> println(p)
MutablePoint(x=10, y=20)
复制代码

二、“in”的约定

集合支持的另一个运算符是in运算符,用于检查某个对象是否属于集合。相应的函数叫做contains

  • 实现in的约定
data class Rectangle(val upperLeft: Point, val lowerRight: Point)

operator fun Rectangle.contains(p: Point): Boolean {
    return p.x in upperLeft.x until  lowerRight.x &&
            p.y in upperLeft.y until lowerRight.y
}

// 测试
>>> val rect = Rectangle(Point(10, 20), Point(50, 50))
>>> println(Point(20, 30) in rect)
true
>>> println(Point(5, 5) in rect)
false
复制代码

in右边的对象将会调用contains函数,in左边的对象将会作为函数入参。

注意“10 .. 20”构建一个普通的区间(闭区间),该区间则包括10到20的所有数字,包括20。“10 until 20” 包括从10到19的数字,但不包括20。

三、rangTo的约定

要创建一个区间,使用 “..”(如1 .. 10)语法。..运算符是调用rangeTo函数的一个简洁方法。

start .. end    ----------->       start.rangeTo(end)
复制代码

rangeTo函数返回一个区间。可以为自己的类定义这个运算符。但是,如果该类实现了`Comparable'接口,那就不需要了。

四、在for循环中使用iterator的约定

for(x in list) { .. }将被转换成list.iterator()的调用,然后就像在java中一样,在它上面重复调用hasNextnext方法。

  • 实现日期区间的迭代器
// 定义
operator fun ClosedRange<LocalDate>.iterator(): Iterator<LocalDate> =
        object : Iterator<LocalDate> {
            var current = start

            override fun hasNext() = current <= endInclusive

            override fun next() = current.apply {
                current = plusDays(1)
            }
        }

// 测试
>>> val newYear = LocalDate.ofYearDay(2017, 1)
>>> val daysOff = newYear.minusDays(1) .. newYear
// 对应的iterator函数实现后,遍历daysOff
>>> for (dayOff in daysOff) { println(dayOff) }
2016-12-31
2017-01-01
复制代码