Kotlin中的Iterator与护士小姐姐

Kotlin中的Iterator与护士小姐姐_第1张图片
Screen Shot 2020-12-12 at 11.53.12 PM.png

隐喻:护士小姐姐和医生

Kotlin中的Iterator与护士小姐姐_第2张图片
分诊

Iterator如同一个护士小姐姐。
护士小姐姐在诊室外组织病人们排队。
病人们怎么排序?
挂号的顺序、递交病历本的顺序还是颜值,
护士小姐姐说了算。

Iterator的调用者如同医生。
医生坐在诊室里面,专心致志的给她面前的病人看病。
看完之后只需要喊一声:“下一个”。

接下来我们一起看看Iterator是怎么做的。

从一个优雅的例子开始

Kotlin官方文档中有个例子Iterators,简洁优雅。

class Animal(val name: String)

class Zoo(val animals: List) {

    operator fun iterator(): Iterator {             // 1
        return animals.iterator()                           // 2
    }
}

fun main() {

    val zoo = Zoo(listOf(Animal("zebra"), Animal("lion")))

    for (animal in zoo) {                                   // 3
        println("Watch out, it's a ${animal.name}")
    }

}

透过它我们来看看Iterator的优点。
它将数据结构封装在类的内部,调用者无需关心数据是如何存储的。
并且有迭代器的类可以使用for in循环访问,这使得代码非常接近自然语言,易读而便于维护。

简约的文档并不能使我感到满足

官网中例子对应的英文文档相当的简约,即便如此,所谓的kotlin中文网也没有对应的译文。我只好手动翻译:

迭代器
在你的类中,你能通过实现iterator操作符来定义你自己的迭代器。
...此处省略刚才举过的例子代码十几行...
1. 在类中定义一个迭代器。它必须命名为iterator,并且被关键字operator修饰。
2. 返回的迭代器,需包括下列成员函数
· next(): Animal
· hasNext(): Boolean
3. 使用自定义迭代器循环遍历动物园中的动物
迭代器可以用扩展函数的形式声明。

例子中的迭代器相当简单,仅仅是把自己的list成员的iterator透传出来。如果有特殊需求怎么办呢?

经过摸索发现我们需要定义自己的Iterator类。

大体思路:

如果当前数据中没有合适的iterator,就自己定义一个内部类xxIterator,
这个内部类,可以访问到外部类的所有数据,
这个内部类,可以将遍历外部数据相关的逻辑和遍历状态封装起来。
这个内部类,符合Iterator接口规范(实现next和hasNext函数)
最后将这个内部类通过外部类的iterator接口提供给外面用。

关键代码如下:

class Hospital(val femalePatients: List, val malePatients: List) {

    inner class PatientIterator : Iterator {
        //...
        public override operator fun next(): Patient {
            //...
        }

        public override operator fun hasNext(): Boolean {
            //...
        }
    }

    operator fun iterator(): Iterator = PatientIterator()
}

需要注意的细节:

  1. Kotlin中内嵌类需要 inner修饰
  2. 重载成员需要用override修饰
  3. next、hasNext和iterator 需要用operator修饰

完整代码:

package top.ovo.hospital

// 基类病人 
open class Patient(val name: String) {
    open fun name(): String {
        return name
    }
}

// 男病人
class MalePatient(name: String) : Patient(name) {
    override fun name(): String {
        return "$name\t♂"
    }
}

// 女病人
class FemalePatient(name: String) : Patient(name) {
    override fun name(): String {
        return "$name\t♀"
    }
}

// 医院
class Hospital(val femalePatients: List, val malePatients: List) {

    inner class PatientIterator : Iterator {
        private var femalePatientsIndex: Int = 0
        private var malePatientsIndex: Int = 0

        public override operator fun next(): Patient {

            if (femalePatientsIndex  = PatientIterator()
}

fun main() {

    println("--- 这里是分诊台 ---")

    val hospital = Hospital(
        listOf(
            FemalePatient("赵丽影"),
            FemalePatient("关晓童"),
            FemalePatient(" 杨蜜 "),
            FemalePatient(" 柳颜 ")
        ),
        listOf(
            MalePatient(" 马匀 "),
            MalePatient("张一明"),
            MalePatient("马化疼"),
            MalePatient("李彦红")
        )
    )

    for (Patient in hospital) {
        println("${Patient.name()}\t正在排队")
    }
}


代码说明:

首先我们声明一个基类“Patient”有共同的属性“name”。

然后按照性别派生了MalePatient和FemalePatient,name()方法中提供了个性化的返回。

“Hospital”类中包含两个队列,malePatients和femalePatients

内嵌类“PatientIterator”中封装了对两个病人列表的遍历逻辑:

先遍历女病人列表,然后遍历男病人列表。

如果都遍历完则通过hasNext返回没有病人了

“Hospital”中iterator()函数创建并返回了PatientIterator的实例

最后的main函数中我们使用forIn语句循环hospital实例得到队列中的每个病人。

运行结果:

$  ~/.sdkman/candidates/kotlin/current/bin/kotlinc hospital.kt -include-runtime -d hospital.jar && java -jar hospital.jar
--- 这里是分诊台 ---
赵丽影  ♀       正在排队
关晓童  ♀       正在排队
 杨蜜   ♀       正在排队
 柳颜   ♀       正在排队
 马匀   ♂       正在排队
张一明  ♂       正在排队
马化疼  ♂       正在排队
李彦红  ♂       正在排队

你可能感兴趣的:(Kotlin中的Iterator与护士小姐姐)