Kotlin中的Iterator與護(hù)士小姐姐

Screen Shot 2020-12-12 at 11.53.12 PM.png

隱喻:護(hù)士小姐姐和醫(yī)生

分診

Iterator如同一個(gè)護(hù)士小姐姐。
護(hù)士小姐姐在診室外組織病人們排隊(duì)。
病人們?cè)趺磁判颍?br> 掛號(hào)的順序、遞交病歷本的順序還是顏值,
護(hù)士小姐姐說了算。

Iterator的調(diào)用者如同醫(yī)生。
醫(yī)生坐在診室里面,專心致志的給她面前的病人看病。
看完之后只需要喊一聲:“下一個(gè)”。

接下來我們一起看看Iterator是怎么做的。

從一個(gè)優(yōu)雅的例子開始

Kotlin官方文檔中有個(gè)例子Iterators,簡(jiǎn)潔優(yōu)雅。

class Animal(val name: String)

class Zoo(val animals: List<Animal>) {

    operator fun iterator(): Iterator<Animal> {             // 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的優(yōu)點(diǎn)。
它將數(shù)據(jù)結(jié)構(gòu)封裝在類的內(nèi)部,調(diào)用者無需關(guān)心數(shù)據(jù)是如何存儲(chǔ)的。
并且有迭代器的類可以使用for in循環(huán)訪問,這使得代碼非常接近自然語言,易讀而便于維護(hù)。

簡(jiǎn)約的文檔并不能使我感到滿足

官網(wǎng)中例子對(duì)應(yīng)的英文文檔相當(dāng)?shù)暮?jiǎn)約,即便如此,所謂的kotlin中文網(wǎng)也沒有對(duì)應(yīng)的譯文。我只好手動(dòng)翻譯:

迭代器
在你的類中,你能通過實(shí)現(xiàn)iterator操作符來定義你自己的迭代器。
...此處省略剛才舉過的例子代碼十幾行...
1. 在類中定義一個(gè)迭代器。它必須命名為iterator,并且被關(guān)鍵字operator修飾。
2. 返回的迭代器,需包括下列成員函數(shù)
· next(): Animal
· hasNext(): Boolean
3. 使用自定義迭代器循環(huán)遍歷動(dòng)物園中的動(dòng)物
迭代器可以用擴(kuò)展函數(shù)的形式聲明。

例子中的迭代器相當(dāng)簡(jiǎn)單,僅僅是把自己的list成員的iterator透?jìng)鞒鰜?。如果有特殊需求怎么辦呢?

經(jīng)過摸索發(fā)現(xiàn)我們需要定義自己的Iterator類。

大體思路:

如果當(dāng)前數(shù)據(jù)中沒有合適的iterator,就自己定義一個(gè)內(nèi)部類xxIterator,
這個(gè)內(nèi)部類,可以訪問到外部類的所有數(shù)據(jù),
這個(gè)內(nèi)部類,可以將遍歷外部數(shù)據(jù)相關(guān)的邏輯和遍歷狀態(tài)封裝起來。
這個(gè)內(nèi)部類,符合Iterator接口規(guī)范(實(shí)現(xiàn)next和hasNext函數(shù))
最后將這個(gè)內(nèi)部類通過外部類的iterator接口提供給外面用。

關(guān)鍵代碼如下:

class Hospital(val femalePatients: List<FemalePatient>, val malePatients: List<MalePatient>) {

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

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

    operator fun iterator(): Iterator<Patient> = PatientIterator()
}

需要注意的細(xì)節(jié):

  1. Kotlin中內(nèi)嵌類需要 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♀"
    }
}

// 醫(yī)院
class Hospital(val femalePatients: List<FemalePatient>, val malePatients: List<MalePatient>) {

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

        public override operator fun next(): Patient {

            if (femalePatientsIndex <femalePatients.size) {
                return femalePatients.get(femalePatientsIndex++)
            }
            return malePatients.get(malePatientsIndex++)
        }

        public override operator fun hasNext(): Boolean {

            return femalePatientsIndex < femalePatients.size ||
                malePatientsIndex < malePatients.size
        }
    }

    operator fun iterator(): Iterator<Patient> = PatientIterator()
}

fun main() {

    println("--- 這里是分診臺(tái) ---")

    val hospital = Hospital(
        listOf(
            FemalePatient("趙麗影"),
            FemalePatient("關(guān)曉童"),
            FemalePatient(" 楊蜜 "),
            FemalePatient(" 柳顏 ")
        ),
        listOf(
            MalePatient(" 馬勻 "),
            MalePatient("張一明"),
            MalePatient("馬化疼"),
            MalePatient("李彥紅")
        )
    )

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


代碼說明:

首先我們聲明一個(gè)基類“Patient”有共同的屬性“name”。

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

“Hospital”類中包含兩個(gè)隊(duì)列,malePatients和femalePatients

內(nèi)嵌類“PatientIterator”中封裝了對(duì)兩個(gè)病人列表的遍歷邏輯:

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

如果都遍歷完則通過hasNext返回沒有病人了

“Hospital”中iterator()函數(shù)創(chuàng)建并返回了PatientIterator的實(shí)例

最后的main函數(shù)中我們使用forIn語句循環(huán)hospital實(shí)例得到隊(duì)列中的每個(gè)病人。

運(yùn)行結(jié)果:

$  ~/.sdkman/candidates/kotlin/current/bin/kotlinc hospital.kt -include-runtime -d hospital.jar && java -jar hospital.jar
--- 這里是分診臺(tái) ---
趙麗影  ♀       正在排隊(duì)
關(guān)曉童  ♀       正在排隊(duì)
 楊蜜   ♀       正在排隊(duì)
 柳顏   ♀       正在排隊(duì)
 馬勻   ♂       正在排隊(duì)
張一明  ♂       正在排隊(duì)
馬化疼  ♂       正在排隊(duì)
李彥紅  ♂       正在排隊(duì)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Kotlin 知識(shí)梳理系列文章 Kotlin 知識(shí)梳理(1) - Kotlin 基礎(chǔ)Kotlin 知識(shí)梳理(2) ...
    澤毛閱讀 2,380評(píng)論 1 3
  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,701評(píng)論 9 118
  • 到本章為止,kotlin基本的知識(shí)點(diǎn)都記錄完畢。還有關(guān)于一些泛型和反射的知識(shí)點(diǎn)后續(xù)會(huì)更新上來,知識(shí)點(diǎn)和內(nèi)容來自《K...
    Haife閱讀 3,593評(píng)論 0 6
  • 五、Lambda編程 1.Lambda表達(dá)式和成員引用 Lambda簡(jiǎn)介:作為函數(shù)參數(shù)的代碼塊??梢岳斫鉃楹?jiǎn)化表達(dá)...
    TomyZhang閱讀 539評(píng)論 0 0
  • 參考文檔 https://kotlinlang.org/ Kotlin是一種在Java虛擬機(jī)上運(yùn)行的靜態(tài)類型編程語...
    JunChow520閱讀 509評(píng)論 0 1

友情鏈接更多精彩內(nèi)容