人生苦短,我選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?