Kotlin Koans學(xué)習(xí)筆記(3)

這是Kotlin Koans學(xué)習(xí)筆記的第三篇。
第一篇在這里址晕,第二篇在這里

這一部分一共7個(gè)任務(wù)顿锰,所有的任務(wù)都是圍繞日期展開(kāi)斩箫,日期對(duì)象具有年吏砂、月、日三個(gè)屬性:

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int)

這一部分的任務(wù)主要是練習(xí)Kotlin對(duì)象運(yùn)算符的重載乘客。

Kotlin有固定數(shù)目的符號(hào)運(yùn)算符狐血,我們可以很簡(jiǎn)單的將這些運(yùn)算符應(yīng)用到任何一個(gè)類。具體的實(shí)現(xiàn)方法就是:對(duì)需要實(shí)現(xiàn)的運(yùn)算符以Kotlin保留的函數(shù)名創(chuàng)建一個(gè)函數(shù)易核,這個(gè)運(yùn)算符就會(huì)自動(dòng)映射到相應(yīng)的函數(shù)匈织。為了提醒編譯器我們重載了一個(gè)運(yùn)算符,我們需要將重載函數(shù)標(biāo)記為operator牡直。 在后面的任務(wù)中我們可以看到這一特性將大大的提高代碼的簡(jiǎn)潔性和可讀性缀匕。

這里先列出全部的運(yùn)算符和它對(duì)應(yīng)的函數(shù)。某一個(gè)類如果要使用某一個(gè)運(yùn)算符碰逸,就需要實(shí)現(xiàn)相應(yīng)的函數(shù)乡小。

單目運(yùn)算符

表達(dá)式 對(duì)應(yīng)的函數(shù)
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()
a++ a.inc()
a-- a.dec()

雙目運(yùn)算符

表達(dá)式 對(duì)應(yīng)的函數(shù)
a+b a.plus(b)
a-b a.minus(b)
a*b a.times(b)
a/b a.div(b)
a%b a.mod(b)
a..b a.rangeTo(b)
a in b b.contains(a)
a !in b !b.contains(a)
a+=b a.plusAssign(b)
a-=b a.minusAssign(b)
a*=b a.timesAssign(b)
a/=b a.divAssign(b)
a%=b a.modAssign(b)

類數(shù)組(Array-like)運(yùn)算符

表達(dá)式 對(duì)應(yīng)函數(shù)
a[i] a.get(i)
a[i,j] a.get(i,j)
a[i_1, ..., i_n] a.get(i_1, ..., i_n)
a[i]=b a.set(i,b)
a[i,j]=b a.set(i,j,b)
a[i_1, ..., i_n]=b a.set(i_1, ..., i_n, b)

相等運(yùn)算符

表達(dá)式 對(duì)應(yīng)的函數(shù)
a==b a?.equals(b) ?:b === null
a==b !(a?.equals(b) ?:b === null)

函數(shù)調(diào)用

表達(dá)式 對(duì)應(yīng)的函數(shù)
a(i) a.invoke(i)
a(i,j) a.invoke(i,j)
a(i_1, ..., i_n) a.invoke(i_1, ..., i_n)

這一部分的任務(wù)主要就是實(shí)現(xiàn)上面的這些運(yùn)算符函數(shù)。

25.Comparsion

第一個(gè)任務(wù)要求實(shí)現(xiàn)日期對(duì)象大小的比較饵史,如下代碼能夠返回比較結(jié)果:

fun task25(date1: MyDate, date2: MyDate): Boolean {
    //todoTask25()
    return date1 < date2
}

代碼不做任何修改是編譯不過(guò)的满钟,編譯器會(huì)提示MyDate類沒(méi)有實(shí)現(xiàn)相關(guān)的函數(shù)。任務(wù)的提示也很清楚胳喷,MyDate類實(shí)現(xiàn)Comparable接口即可:

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int): Comparable<MyDate> {
    override fun compareTo(other: MyDate) =
        when{
            other.year != year -> year - other.year
            other.month != month -> month - other.month
            else -> dayOfMonth - other.dayOfMonth
        }
}

通過(guò)實(shí)現(xiàn)Comparable接口湃番,就可以在代碼中直接使用大小比較運(yùn)算符。

26.InRange

這個(gè)任務(wù)的要求編碼實(shí)現(xiàn)檢查指定的日期是不是在某一個(gè)日期范圍內(nèi):

fun checkInRange(date: MyDate, first: MyDate, last: MyDate): Boolean {
    //todoTask26_()
    return date in DateRange(first, last)
}

根據(jù)前面的表格知道:a in b翻譯以后就是b.contains(a),所以這里的任務(wù)就轉(zhuǎn)換成實(shí)現(xiàn)DateRange類的contains(d: MyDate):Boolean函數(shù)吭露。DateRange類的定義已經(jīng)給出來(lái)了吠撮,它包含一個(gè)起始日期(start)和一個(gè)截止日期(endInclusive)。由于上一個(gè)任務(wù)已經(jīng)實(shí)現(xiàn)了日期大小的比較讲竿,所以這個(gè)任務(wù)也就很好完成:

operator fun contains(d: MyDate) = (d>=start && d<=endInclusive)

注意函數(shù)定義前面的operator修飾符泥兰。

27.RangeTo

這一個(gè)任務(wù)是要求實(shí)現(xiàn)MyDate類的..運(yùn)算符。..運(yùn)算符最終會(huì)翻譯成rangeTo()函數(shù)题禀,所以本任務(wù)就是實(shí)現(xiàn)MyDate.rangeTo()逾条,由于在上一個(gè)任務(wù)中DateRange已經(jīng)實(shí)現(xiàn)了operator fun contains(d: MyDate)。所以rangeTo()函數(shù)返回一個(gè)DateRange對(duì)象即可:

operator fun MyDate.rangeTo(other: MyDate): DateRange = DateRange(this, other)

fun checkInRange2(date: MyDate, first: MyDate, last: MyDate): Boolean {
    //todoTask27()
    return date in first..last
}

28.ForLoop

任務(wù)要求可以對(duì)DateRange內(nèi)的MyDate執(zhí)行for循環(huán)投剥,也就是要求DataRange實(shí)現(xiàn)Iterable<MyDate>接口师脂。任務(wù)描述中也給出了足夠的提示。日期范圍的迭代以為迭代間隔江锨,所以需要使用已經(jīng)定義好的nextDay()方法吃警,一次遞增一天:

fun MyDate.nextDay() = addTimeIntervals(DAY, 1)

Iterator<MyDate>接口有兩個(gè)方法next()hasNext():

lass DateRange(val start: MyDate, val endInclusive: MyDate) : Iterable<MyDate>{
    
    ...
    
    override fun iterator(): Iterator<MyDate>  = DateIterator(this)
}

class DateIterator(val dateRange:DateRange) : Iterator<MyDate> {
    var current: MyDate = dateRange.start
    override fun next(): MyDate {
        val result = current
        current = current.nextDay()
        return result
    }
    override fun hasNext(): Boolean = current <= dateRange.endInclusive
}

29.OperatorOverloading

這一個(gè)任務(wù)包含兩個(gè)任務(wù),主要練習(xí)重載運(yùn)算符啄育。第一個(gè)小任務(wù)是重載MyDate+運(yùn)算符:

fun task29_1(today: MyDate): MyDate {
   // todoTask29()
    return today + YEAR + WEEK

MyDate可以和一個(gè)時(shí)間間隔相加酌心,所以需要實(shí)現(xiàn)MyDate.plus()函數(shù),以時(shí)間間隔為參數(shù)挑豌,有了上一個(gè)任務(wù)計(jì)算下一天的基礎(chǔ)安券,實(shí)現(xiàn)和時(shí)間間隔相加就比較容易了:

operator fun MyDate.plus(timeInterval: TimeInterval) = addTimeIntervals(timeInterval, 1)

第二個(gè)小任務(wù)在第一個(gè)小任務(wù)的基礎(chǔ)上更進(jìn)一步墩崩,不再是加一個(gè)單一的間隔,要求相加多個(gè)時(shí)間間隔侯勉,如加3年鹦筹,加7個(gè)星期。址貌。铐拐。所以需要先實(shí)現(xiàn)時(shí)間間隔的*運(yùn)算符,將TimeInterval的乘法結(jié)果定義成RepeatedTimeInterval:

class RepeatedTimeInterval(val timeInterval: TimeInterval, val number: Int)
operator fun TimeInterval.times(number: Int) = RepeatedTimeInterval(this, number)

由于這里*運(yùn)算返回的是一個(gè)RepeatedTimeInterval對(duì)象练对,所以還需要實(shí)現(xiàn)MyDateRepeatedTimeInterval相加:

operator fun MyDate.plus(timeIntervals: RepeatedTimeInterval) = addTimeIntervals(timeIntervals.timeInterval, timeIntervals.number)

仍然是使用已有的addTimeIntervals方法遍蟋, 將RepeatedTimeInterval對(duì)象的兩個(gè)屬性作為該方法的兩個(gè)參數(shù)。這樣螟凭,就可以實(shí)現(xiàn)以下這樣的表達(dá)式運(yùn)算:

fun task29_2(today: MyDate): MyDate {
    //todoTask29()
    return today + YEAR * 2 + WEEK * 3 + DAY * 5
}

30.Destructuring Declaration

Kotlin可以將一個(gè)對(duì)象的所有屬性一次賦值給一堆變量:

val (name, age) = person 

這段代碼會(huì)被編譯成:

val name = person.component1()
val age = person.component2()

這就是為什么DataClass會(huì)自動(dòng)生成componentN()函數(shù)虚青。

任務(wù)的要求是判斷一個(gè)日期是否是閏年,第一步需要將時(shí)間的屬性賦值給年螺男、月棒厘、日。這就需要使用destructuring declaration烟号。

<pre>
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int)

fun isLeapDay(date: MyDate): Boolean {
//todoTask30()
val (year, month, dayOfMonth) = date

// 29 February of a leap year
return year % 4 == 0 && month == 2 && dayOfMonth == 29

}
</pre>

注意如果需要使用Destructuring Declaration,類必須聲明為data類型政恍。

31.Invoke

終于到了這一部分的最后一個(gè)任務(wù)汪拥,invoke。Invoke是什么呢篙耗?如果一個(gè)類實(shí)現(xiàn)了invoke()這個(gè)函數(shù)迫筑,那么該類的實(shí)體對(duì)象在調(diào)用這個(gè)函數(shù)時(shí)可以省略函數(shù)名,當(dāng)然invoke函數(shù)必須要有operator修飾符宗弯。先看一下栗子吧:

class customClass{

    operator fun invoke(param: Int){
        println("print in invoke method, param is :"+param)
    }

    operator fun invoke(param: Int, msg: String): customClass{
        println("print in invoke method, param is :"+param+",message is:" + msg)
        return this
    }
}

那么就可以這樣使用:

val printer = customClass()
    printer(2)
    printer.invoke(4)

    printer(12, "first")(23, "second")(46)

執(zhí)行結(jié)果就是:

print in invoke method, param is :2
print in invoke method, param is :4
print in invoke method, param is :12,message is:first
print in invoke method, param is :23,message is:second
print in invoke method, param is :46

回到我們的任務(wù)脯燃,要求如下代碼返回的結(jié)果為4:

fun task31(invokable: Invokable): Int {
    //todoTask31()
    return invokable()()()().getNumberOfInvocations()
}

所以需要實(shí)現(xiàn)Invokableinvoke方法,該方法每調(diào)用一次蒙保,內(nèi)部計(jì)數(shù)器就需要加1辕棚,最后返回計(jì)數(shù)器的值:

class Invokable {
    var numberOfInvocations: Int = 0
    operator fun invoke(): Invokable {
        numberOfInvocations++
        return this
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市邓厕,隨后出現(xiàn)的幾起案子逝嚎,更是在濱河造成了極大的恐慌,老刑警劉巖详恼,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件补君,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡昧互,警方通過(guò)查閱死者的電腦和手機(jī)挽铁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門伟桅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人叽掘,你說(shuō)我怎么就攤上這事楣铁。” “怎么了够掠?”我有些...
    開(kāi)封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵民褂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我疯潭,道長(zhǎng)赊堪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任竖哩,我火速辦了婚禮哭廉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘相叁。我一直安慰自己遵绰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布增淹。 她就那樣靜靜地躺著椿访,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虑润。 梳的紋絲不亂的頭發(fā)上成玫,一...
    開(kāi)封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音拳喻,去河邊找鬼哭当。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冗澈,可吹牛的內(nèi)容都是我干的钦勘。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼亚亲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼彻采!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起捌归,我...
    開(kāi)封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颊亮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后陨溅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體终惑,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年门扇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雹有。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偿渡。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖霸奕,靈堂內(nèi)的尸體忽然破棺而出溜宽,到底是詐尸還是另有隱情,我是刑警寧澤质帅,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布适揉,位于F島的核電站,受9級(jí)特大地震影響煤惩,放射性物質(zhì)發(fā)生泄漏嫉嘀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一魄揉、第九天 我趴在偏房一處隱蔽的房頂上張望剪侮。 院中可真熱鬧,春花似錦洛退、人聲如沸瓣俯。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)彩匕。三九已至,卻和暖如春媒区,著一層夾襖步出監(jiān)牢的瞬間驼仪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工驻仅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谅畅,地道東北人登渣。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓噪服,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親胜茧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粘优,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容