掌握Kotlin標準函數(shù):run, with, let, also and apply

原文鏈接

Kotlin的一些標準函數(shù)非常相似望几,我們不確定使用哪個函數(shù)伍掀。在這里我將介紹一個簡單的方法來清楚地區(qū)分他們的差異和如何選擇使用掰茶。

范圍函數(shù)

我重點關注run, with, T.run, T.let, T.also and T.apply函數(shù)。我稱他們?yōu)榉秶瘮?shù)蜜笤,因為我認為他們的主要功能是為調(diào)用函數(shù)提供一個內(nèi)部范圍濒蒋。

run函數(shù)是說明最簡單的范圍方法

fun test() {
    var mood = "I am sad"

    run {
        val mood = "I am happy"
        println(mood) // I am happy
    }
    println(mood)  // I am sad
}

有了這個函數(shù),在test函數(shù)內(nèi)部把兔,你可以有一個單獨的范圍沪伙,mood在重新定義為I am happy并打印之前,它被完全封閉在run范圍內(nèi)县好。

這個范圍函數(shù)本身似乎不是很有用围橡。但是相比范圍,還有一點不錯的是缕贡,它返回范圍內(nèi)最后一個對象翁授。

因此拣播,下面代碼將是很純潔的,我們可以像下面一樣收擦,將show()方法應用到兩個 view贮配,而不是 調(diào)用兩次。

run {
      if (firstTimeView) introView else normalView
    }.show()

范圍函數(shù)的3個屬性

為了使范圍函數(shù)更有趣塞赂,讓我用3個屬性將他們的行為分類泪勒,并且使用這些屬性來區(qū)分它們。

1.正常vs.擴展函數(shù)

如果我們看看定義减途,with并且T.run這兩個函數(shù)實際上非常相似酣藻。下面示例實現(xiàn)功能是一樣的。

with(webview.settings) {
    javaScriptEnabled = true
    databaseEnabled = true
}
// 相似
webview.settings.run {
    javaScriptEnabled = true
    databaseEnabled = true
}

然而鳍置,它們的不同之處在于with是正常函數(shù),而T.run是擴展函數(shù)送淆。

那么問題是税产,每個的優(yōu)點是什么?

想象一下偷崩,如果webview.settings可能是空的辟拷,那么看起來就像下面一樣了。

with(webview.settings) {
      this?.javaScriptEnabled = true
      this?.databaseEnabled = true
   }
}

webview.settings?.run {
    javaScriptEnabled = true
    databaseEnabled = true
}

在這種情況下阐斜,顯然T.run擴展功能比較好衫冻,因為在使用之前我們可以判空。

2.This vs. it參數(shù)

如果我們看看定義谒出,隅俘,T.run并且T.let這兩個函數(shù)除了接受參數(shù)的方式不一樣外幾乎是一樣的。以下兩個函數(shù)的邏輯是相同的笤喳。

stringVariable?.run {
      println("The length of this String is $length")
}

stringVariable?.let {
      println("The length of this String is ${it.length}")
}

如果你檢查T.run函數(shù)簽名为居,你會注意到T.run只是作為擴展函數(shù)調(diào)用block: T.()。因此杀狡,所有的范圍內(nèi)蒙畴,T可以被稱為this。在編程中呜象,this大部分時間可以省略膳凝。因此,在我們上面的例子中恭陡,我們可以在println聲明中使用$length蹬音,而不是${this.length}。我把這稱為傳遞this參數(shù)子姜。

然而祟绊,對于T.let函數(shù)簽名楼入,你會注意到T.let把自己作為參數(shù)傳遞進去,即block: (T)牧抽。因此嘉熊,這就像傳遞一個lambda參數(shù)。它可以在作用域范圍內(nèi)使用it作為引用扬舒。所以我把這稱為傳遞it參數(shù)阐肤。

從上面看,它似乎T.run是更優(yōu)越讲坎,因為T.let更隱含孕惜,但是這是T.let函數(shù)有一些微妙的優(yōu)勢如下:

  • T.let相比外部類函數(shù)/成員,使用給定的變量函數(shù)/成員提供了更清晰的區(qū)分
  • this不能被省略的情況下晨炕,例如當它作為函數(shù)的參數(shù)被傳遞時itthis更短衫画,更清晰。
  • T.let允許使用更好的變量命名瓮栗,你可以轉(zhuǎn)換it為其他名稱削罩。
stringVariable?.let {
      nonNullString ->
      println("The non null string is $nonNullString")
}

3.返回當前類型 vs.其他類型

現(xiàn)在,我們來看看T.letT.also费奸,如果我們看它們的內(nèi)部函數(shù)范圍弥激,使用起來是一樣的

stringVariable?.let {
      println("The length of this String is ${it.length}")
}
stringVariable?.also {
      println("The length of this String is ${it.length}")
}

然而,他們微妙的不同是他們的返回值愿阐。T.let返回不同類型的值微服,而T.also返回T本身即this

兩者對于鏈接函數(shù)都是有用的缨历,通過T.let你可以演變操作以蕴,通過T.also你在同一個變量this上執(zhí)行操作。

簡單的例子如下

val original = "abc"
// 改變值并且傳遞到下一鏈條
original.let {
    println("The original String is $it") // "abc"
    it.reversed() // 改變參數(shù)并且傳遞到下一鏈條
}.let {
    println("The reverse String is $it") // "cba"
    it.length   // 改變類型
}.let {
    println("The length of the String is $it") // 3
}
// 錯誤
// 在鏈中發(fā)送相同的值(打印的答案是錯誤的)
original.also {
    println("The original String is $it") // "abc"
    it.reversed() // 即使我們改變它,也是沒用的
}.also {
    println("The reverse String is ${it}") // "abc"
    it.length  // 即使我們改變它,也是沒用的
}.also {
    println("The length of the String is ${it}") // "abc"
}

// also通過修改原始字符串也可以達到同樣目的
// 在鏈中發(fā)送相同的值
original.also {
    println("The original String is $it") // "abc"
}.also {
    println("The reverse String is ${it.reversed()}") // "cba"
}.also {
    println("The length of the String is ${it.length}") // 3
}

在上面看來T.also好像毫無意義铅檩,因為我們可以很容易地將它們組合成一個功能塊蛋欣。但仔細想想,它也有一些優(yōu)點:

  1. 它可以在相同的對象上提供一個非常清晰的分離過程,即制作更小的功能部分。
  2. 在使用之前,它可以實現(xiàn)非常強大的自我操縱伴鳖,實現(xiàn)鏈條建設者操作(builder 模式)。

當兩者結(jié)合在一起時徙硅,即一個自我演變榜聂,一個自我保留,可以變得非常強大嗓蘑,例如下面

// 正常方法
fun makeDir(path: String): File  {
    val result = File(path)
    result.mkdirs()
    return result
}
// 改進方法
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }

所有的屬性

通過說明這3個屬性须肆,我們應該可以了解這些函數(shù)的行為了匿乃。讓我們再來看看T.apply函數(shù),因為上面沒有提到豌汇。這3個屬性在T.apply定義如下...

  1. 這是一個擴展函數(shù)
  2. this作為參數(shù)傳遞幢炸。
  3. 它返回this(即它本身)

因此,可以想象拒贱,它可以像下面一樣被使用

// 正常方法
fun createInstance(args: Bundle) : MyFragment {
    val fragment = MyFragment()
    fragment.arguments = args
    return fragment
}
// 改進方法
fun createInstance(args: Bundle) 
              = MyFragment().apply { arguments = args }

或者我們也可以創(chuàng)建鏈式調(diào)用宛徊。

// 正常方法
fun createIntent(intentData: String, intentAction: String): Intent {
    val intent = Intent()
    intent.action = intentAction
    intent.data=Uri.parse(intentData)
    return intent
}
//  改進實現(xiàn)
fun createIntent(intentData: String, intentAction: String) =
        Intent().apply { action = intentAction }
                .apply { data = Uri.parse(intentData) }

函數(shù)選擇

因此,顯然逻澳,有了這三個屬性闸天,我們現(xiàn)在可以對上述函數(shù)進行相應的分類。在此基礎上斜做,我們可以在下面形成一個決策樹苞氮,可以幫助我們決定使用哪個函數(shù)。

img

希望上面的決策樹可以清晰說明函數(shù)區(qū)別陨享,也簡化您的決策葱淳,使您能夠恰當掌握這些函數(shù)的使用。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抛姑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子艳狐,更是在濱河造成了極大的恐慌定硝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毫目,死亡現(xiàn)場離奇詭異蔬啡,居然都是意外死亡,警方通過查閱死者的電腦和手機镀虐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門箱蟆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刮便,你說我怎么就攤上這事空猜。” “怎么了恨旱?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵辈毯,是天一觀的道長。 經(jīng)常有香客問我搜贤,道長谆沃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任仪芒,我火速辦了婚禮唁影,結(jié)果婚禮上耕陷,老公的妹妹穿的比我還像新娘。我一直安慰自己据沈,他們只是感情好哟沫,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卓舵,像睡著了一般南用。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掏湾,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天裹虫,我揣著相機與錄音,去河邊找鬼融击。 笑死筑公,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的尊浪。 我是一名探鬼主播匣屡,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拇涤!你這毒婦竟也來了捣作?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鹅士,失蹤者是張志新(化名)和其女友劉穎券躁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掉盅,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡也拜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了趾痘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慢哈。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖永票,靈堂內(nèi)的尸體忽然破棺而出卵贱,到底是詐尸還是另有隱情,我是刑警寧澤瓦侮,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布艰赞,位于F島的核電站,受9級特大地震影響肚吏,放射性物質(zhì)發(fā)生泄漏方妖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一罚攀、第九天 我趴在偏房一處隱蔽的房頂上張望党觅。 院中可真熱鬧雌澄,春花似錦、人聲如沸杯瞻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽魁莉。三九已至睬涧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旗唁,已是汗流浹背畦浓。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留检疫,地道東北人讶请。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像屎媳,于是被迫代替她去往敵國和親夺溢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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