幫助理解Kotlin函數(shù)的3個概念

kotlin&android

人生苦短,我選Kotlin
——筆者

Kotlin相比Java很年輕僧凤,也更有潛力豆巨,其對函數(shù)式編程的支持也讓其代碼更加簡潔剩辟,但在理解函數(shù)式編程的過程中,總會有些障礙往扔,比如筆者在看到apply和with這兩個方法的時候贩猎,就很奇怪,看了源碼就更加糊涂了萍膛,本文以剖析apply和with這兩個方法為線索吭服,介紹下kotlin中函數(shù)式編程相關(guān)的幾個概念。

函數(shù)類型(function type)

在函數(shù)式語言中蝗罗,函數(shù)作為一種類型可以在函數(shù)間傳遞艇棕,那么如何區(qū)別不同的函數(shù)的類型呢?
將函數(shù)的入?yún)⒑头祷刂荡埽鳛橐环N函數(shù)類型沼琉,比如:
(Int) -> Int 是一個函數(shù)類型,它的傳入?yún)?shù)為Int桩匪,返回類型為Int打瘪,滿足這個條件的函數(shù)為同一種類型的函數(shù)。

fun double(x: Int): Int {
    return 2 * x
}

比如double這個函數(shù)的類型為(Int) -> Int,它的入?yún)⑹荌nt傻昙,返回值為Int.

由于函數(shù)為一種類型瑟慈,我們可以像定義Int值一樣定義一個函數(shù)類型的變量:

val double: (Int) -> Int = fun(value: Int): Int {
    return 2 * value
}

double的類型為函數(shù),函數(shù)類型為(Int) -> Int
函數(shù)作為變量可以傳遞:

val doubleCopy = double
doubleCopy(1)

此時doubleCopy的類型和double的類型相同

高階函數(shù) (high order function)

如果一個函數(shù)將一個函數(shù)作為參數(shù)屋匕,或者返回一個函數(shù)葛碧,那么這種函數(shù)叫做high order function(高階函數(shù))

前面提到函數(shù)可以作為變量進行傳遞,將函數(shù)作為參數(shù)傳遞到另一個函數(shù)中过吻,也是允許的进泼,比如下面的例子:

// lock為高階函數(shù)蔗衡,接受2個參數(shù),類型分別為:Lock乳绕,() -> T
// 前者為常見的Lock類型绞惦,后者為一個函數(shù)類型,這個類型的函數(shù)入?yún)榭昭蟠耄祷刂禐門
fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    }
    finally {
        lock.unlock()
    }
}
// 調(diào)用方法
lock (lock,{sharedResource.operation()})
// 在kotlin中济蝉,如果一個函數(shù)的最后一個參數(shù)為函數(shù),則可以將這個函數(shù)的函數(shù)體放到括號外面菠发,就是常見的下面這種寫法
lock (lock) {
    sharedResource.operation()
}

function-with-receiver type

kotlin中存在一個比較特殊的函數(shù)類型定義王滤,它指定了函數(shù)的receiver(看起來和擴展方法比較像)

理解這個很關(guān)鍵,kotlin中很多基本的函數(shù)都是基于這種函數(shù)類型來實現(xiàn)的滓鸠。

比如下面的代碼段中雁乡,聲明了intToLong這個函數(shù)的receiver類型為Int,只有Int類型的對象(A)可以調(diào)用intToLong這個方法糜俗,并且在intToLong函數(shù)體內(nèi)踱稍,可以通過this來調(diào)用A中的函數(shù)

val intToLong: Int.() -> Long = { toLong() }

上面的代碼段中,實際上調(diào)用的是Int類型本身定義的toLong方法:

//this可以省略掉
val intToLong: Int.() -> Long = { this.toLong() }
//可以編譯通過悠抹,調(diào)用時珠月,上面一句中的this即為3
val Long a = 3.intToLong()
//不可以編譯通過,intToLong聲明了receiver楔敌,只能被Int類型調(diào)用
val Long b = "3".intToLong()

分析下appy和with方法

這兩個方法是kotlin中常用的方法桥温,可以簡化代碼,讓邏輯更加清晰整潔梁丘,但直接理解起來會有點繞侵浸,如果你看懂了前面講到的幾個概念之后,appy和with方法理解起來就相對容易很多

apply

apply是kotlin中常用的方法氛谜,它的官方文檔中的定義是這樣的:

apply:Calls the specified function block with this value as its receiver and returns this value.

我們來看下他的實現(xiàn)代碼:

public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

這里結(jié)合前面的函數(shù)相關(guān)概念掏觉,對這個方法進行分析:

apply為高階函數(shù),它接受一個參數(shù)block值漫,類型為 T.() -> Unit,在apply的函數(shù)體內(nèi)澳腹,調(diào)用了傳入的block這個函數(shù),然后返回調(diào)用apply函數(shù)的對象實例杨何。

需要注意的是酱塔,block函數(shù)的類型為 function-with-receiver ,在block函數(shù)體內(nèi)危虱,可以通過this訪問到T類型的實例羊娃。

//調(diào)用方法
fun getDeveloper(): Developer {
    return Developer().apply {
        developerName = "Amit Shekhar"
        developerAge = 22
    }
}
// 等同于下面這個方法
fun getDeveloper(): Developer {
    //apply 方法返回新創(chuàng)建的Developer()
    return Developer().apply {
        //this 為新創(chuàng)建的Developer(),可省略
        this.developerName = "Amit Shekhar"
        this.developerAge = 22
    }
}

with

with也是比較常用的方法埃跷,它的定義是這樣的:

Calls the specified function block with the given receiver as its receiver and returns its result.

它的實現(xiàn)代碼也只有一行

public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()

with為高階函數(shù)蕊玷,接收兩個參數(shù):receiver邮利,類型為T,block 類型為 T.() -> R,為function-with-receiver type垃帅,只能被T類型的對象調(diào)用延届,同樣,在block方法體內(nèi)贸诚,可以通過this來調(diào)用到receiver方庭。with返回的類型為R,和block的返回類型相同

fun getPersonFromDeveloper(developer: Developer): Person {
    return with(developer) {
        Person(developerName, developerAge)
    }
}

參考:
learn kotlin apply vs with
what is a receiver in kotlin
What is a purpose of Lambda's with Receiver?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酱固,一起剝皮案震驚了整個濱河市械念,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌媒怯,老刑警劉巖订讼,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件髓窜,死亡現(xiàn)場離奇詭異扇苞,居然都是意外死亡,警方通過查閱死者的電腦和手機寄纵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門鳖敷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人程拭,你說我怎么就攤上這事定踱。” “怎么了恃鞋?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵崖媚,是天一觀的道長。 經(jīng)常有香客問我恤浪,道長畅哑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任水由,我火速辦了婚禮荠呐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘砂客。我一直安慰自己泥张,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布鞠值。 她就那樣靜靜地躺著媚创,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彤恶。 梳的紋絲不亂的頭發(fā)上筝野,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天晌姚,我揣著相機與錄音,去河邊找鬼歇竟。 笑死挥唠,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的焕议。 我是一名探鬼主播宝磨,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼盅安!你這毒婦竟也來了唤锉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤别瞭,失蹤者是張志新(化名)和其女友劉穎窿祥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝙寨,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡晒衩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了墙歪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片听系。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖虹菲,靈堂內(nèi)的尸體忽然破棺而出靠胜,到底是詐尸還是另有隱情,我是刑警寧澤毕源,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布浪漠,位于F島的核電站,受9級特大地震影響霎褐,放射性物質(zhì)發(fā)生泄漏址愿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一瘩欺、第九天 我趴在偏房一處隱蔽的房頂上張望必盖。 院中可真熱鬧,春花似錦俱饿、人聲如沸歌粥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽失驶。三九已至,卻和暖如春枣购,著一層夾襖步出監(jiān)牢的瞬間嬉探,已是汗流浹背擦耀。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涩堤,地道東北人眷蜓。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像胎围,于是被迫代替她去往敵國和親吁系。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

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