Kotlin修煉指南

Kotlin修煉指南

作用域函數(shù)

作用域函數(shù)是Kotlin中的一個(gè)非常有用的函數(shù),它主要分為兩種邪码,一種是拓展函數(shù)式宾尚,另一種是頂層函數(shù)式孝情。作用域函數(shù)的主要功能是為調(diào)用函數(shù)提供一個(gè)內(nèi)部范圍只锭,同時(shí)結(jié)合kotlin的語(yǔ)法糖提供一些便捷操作著恩。

作用域函數(shù)主要有下面這幾種,它們的主要區(qū)別就是函數(shù)體內(nèi)使用對(duì)象和返回值的區(qū)別蜻展。

  • run

函數(shù)體內(nèi)使用this代替本對(duì)象喉誊。返回值為函數(shù)最后一行或者return指定的表達(dá)式

  • let

函數(shù)內(nèi)使用it代替本對(duì)象。返回值為函數(shù)最后一行或者return指定的表達(dá)式铺呵。

  • apply

函數(shù)內(nèi)使用this代替本對(duì)象裹驰。返回值為本對(duì)象隧熙。

  • also

函數(shù)內(nèi)使用it代替本對(duì)象片挂。返回值為本對(duì)象。

  • takeIf

條件為真返回對(duì)象本身否則返回null贞盯。

  • takeUnless

條件為真返回null否則返回對(duì)象本身音念。

  • with

with比較特殊,不是以擴(kuò)展方法的形式存在的躏敢,而是一個(gè)頂級(jí)函數(shù)闷愤。
傳入?yún)?shù)為對(duì)象,函數(shù)內(nèi)使用this代替對(duì)象件余。
返回值為函數(shù)最后一行或者return指定的表達(dá)式讥脐。

  • repeat

將函數(shù)體執(zhí)行多次。

通過(guò)表格進(jìn)行下總結(jié)啼器,如下所示旬渠。

操作符 this/it 返回值
let it 最后一行或者return指定的表達(dá)式
with it 最后一行或者return指定的表達(dá)式
run this 最后一行或者return指定的表達(dá)式
also this 上下文對(duì)象
apply this 上下文對(duì)象

下面通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)演示下這些作用域函數(shù)的基本使用方式。

class TestBean {
    var name: String = "xuyisheng"
    var age: Int = 18
}
fun main(args: Array<String>) {
    val test = TestBean()
    val resultRun = test.run {
        name = "xys"
        age = 3
        println("Run內(nèi)部 $this")
        age
    }
    println("run返回值 $resultRun")
    val resultLet = test.let {
        it.name = "xys"
        it.age = 3
        println("let內(nèi)部 $it")
        it.age
    }
    println("let返回值 $resultLet")
    val resultApply = test.apply {
        name = "xys"
        age = 3
        println("apply內(nèi)部 $this")
        age
    }
    println("apply返回值 $resultApply")
    val resultAlso = test.also {
        it.name = "xys"
        it.age = 3
        println("also內(nèi)部 $it")
        it.age
    }
    println("also返回值 $resultAlso")
    val resultWith = with(test) {
        name = "xys"
        age = 3
        println("with內(nèi)部 $this")
        age
    }
    println("with返回值 $resultWith")
    test.age = 33
    val resultTakeIf = test.takeIf {
        it.age > 3
    }
    println("takeIf $resultTakeIf")
    val resultTakeUnless = test.takeUnless {
        it.age > 3
    }
    println("takeUnless $resultTakeUnless")
}

執(zhí)行結(jié)果如下所示端壳。

Run內(nèi)部 TestBean@27c170f0
run返回值 3
let內(nèi)部 TestBean@27c170f0
let返回值 3
apply內(nèi)部 TestBean@27c170f0
apply返回值 TestBean@27c170f0
also內(nèi)部 TestBean@27c170f0
also返回值 TestBean@27c170f0
with內(nèi)部 TestBean@27c170f0
with返回值 3
takeIf TestBean@27c170f0
takeUnless null

官網(wǎng)提供了一張圖來(lái)幫助開(kāi)發(fā)者選擇合適的作用域函數(shù)告丢,如下所示。

file

頂級(jí)函數(shù)使用場(chǎng)景

run损谦、with岖免、repeat,是比較常用的3個(gè)頂級(jí)函數(shù)照捡,它們是區(qū)別于其它幾種拓展函數(shù)類型的颅湘,它們的使用也比較簡(jiǎn)單,示例代碼如下所示栗精。

  • run
fun testRun() {
    var str = "I am xys"
    run {
        val str = "I am zj"
        println(str) // I am xys
    }
    println(str)  // I am zj
}

可以發(fā)現(xiàn)闯参,run頂級(jí)函數(shù)提供了一個(gè)獨(dú)立的作用域,可以在該作用域內(nèi)完整的使用全新的變量和屬性。

  • repeat
repeat(5){
    print("repeat")
}

repeat比較簡(jiǎn)單赢赊,直接將函數(shù)體按指定次數(shù)執(zhí)行乙漓。

  • with

前面的代碼已經(jīng)演示過(guò)with如何使用。

with(ArrayList<String>()) {
    add("a")
    add("b")
    add("c")
    println("this = " + this)
    this
}

要注意的是其返回值是根據(jù)return的類型或者最后一行代碼來(lái)進(jìn)行判斷的释移。

拓展函數(shù)使用場(chǎng)景

?.結(jié)合拓展函數(shù)

Kotlin的?操作符和作用域函數(shù)的拓展函數(shù)可以非常方便的進(jìn)行對(duì)象的判空及后續(xù)處理叭披,例如下面的例子。

// 對(duì)result進(jìn)行了判空并bindData
result?.let {
    if (it.isNotEmpty()) {
        bindData(it)
    }
}

簡(jiǎn)化對(duì)象的創(chuàng)建

類似apply這樣的作用域函數(shù)玩讳,可以返回this的作用域函數(shù)涩蜘,可以將對(duì)象的創(chuàng)建和屬性的賦值寫(xiě)在一起,簡(jiǎn)化代碼熏纯,類似builder模式同诫,例如下面的這個(gè)例子。

// 使用普通的方法創(chuàng)建一個(gè)Fragment
fun createInstance(args: Bundle) : MyFragment {
    val fragment = MyFragment()
    fragment.arguments = args
    return fragment
}
// 通過(guò)apply來(lái)創(chuàng)建一個(gè)Fragment
fun createInstance(args: Bundle)
    = MyFragment().apply { arguments = args }

再例如下面的實(shí)現(xiàn)樟澜。

// 使用普通的方法創(chuàng)建Intent
fun createIntent(intentData: String, intentAction: String): Intent {
    val intent = Intent()
    intent.action = intentAction
    intent.data = Uri.parse(intentData)
    return intent
}
 
// 通過(guò)apply函數(shù)的鏈?zhǔn)秸{(diào)用創(chuàng)建Intent
fun createIntent(intentData: String, intentAction: String) =
    Intent().apply { action = intentAction }
    .apply { data = Uri.parse(intentData) }

以及下面的實(shí)現(xiàn)误窖。

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

同一對(duì)象的多次操作

在開(kāi)發(fā)中,有些對(duì)象有很多參數(shù)或者方法需要設(shè)置秩贰,但該對(duì)象又沒(méi)用提供builder方式進(jìn)行構(gòu)建霹俺,例如下面的例子。

val linearLayout = LinearLayout(itemView.context).apply {
    orientation = LinearLayout.VERTICAL
    layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT)
}

progressBar.apply {
    progress = newProgress
    visibility = if (newProgress in 1..99) View.VISIBLE else View.GONE
}

不論是let毒费、run丙唧、apply還是其它拓展函數(shù),都可以實(shí)現(xiàn)這樣的需求觅玻,借助it或this想际,可以很方便的對(duì)該對(duì)象的多個(gè)屬性進(jìn)行操作。

不過(guò)這些拓展函數(shù)還是有一些細(xì)微的差別的溪厘,例如T.run和T.let(即使用it和this的區(qū)別)

  • 使用it的作用域函數(shù)胡本,可以使用特定的變量名來(lái)重命名it,從而表達(dá)更清楚的語(yǔ)義桩匪。
  • this在大部分情況下是可以省略的打瘪,比使用it簡(jiǎn)單

例如下面的例子。

stringResult?.let {
      nonNullString ->
      println("The non null string is $nonNullString")
}

通過(guò)對(duì)it的重命名傻昙,語(yǔ)義表達(dá)更加清楚闺骚。

條件操作

借助kotlin的?操作符,可以簡(jiǎn)化很多條件操作妆档,例如下面的幾個(gè)例子僻爽。

url = intent.getStringExtra(EXTRA_URL)?.takeIf { it.isNotEmpty() } ?: run {
    toast("url空")
    activity.finish()
}

上面的代碼演示了【從intent中取出url并在url為空時(shí)的操作】。

test.takeIf { it.name.isNotEmpty() }?.also { print("name is $it.name") } ?: print("name empty")

上面代碼演示了【從test中取出name贾惦,不為空的時(shí)候和為空的時(shí)候的操作】胸梆。

鏈?zhǔn)秸{(diào)用

作用域函數(shù)的一個(gè)非常方便的作用就是通過(guò)其返回值的改變來(lái)組裝鏈?zhǔn)秸{(diào)用敦捧。一個(gè)簡(jiǎn)單示例如下所示。

test.also {
    // todo something
}.apply {
    // todo something
}.name = "xys"

通過(guò)let來(lái)改變返回值碰镜,從而將不同的處理通過(guò)鏈?zhǔn)秸{(diào)用串聯(lián)起來(lái)兢卵。

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   // 改變參數(shù)類型并且傳遞到下一鏈條
}.let {
    println("The length of the String is $it") // 3
}

上面的代碼借助let,可以將函數(shù)的返回值不斷進(jìn)行修改绪颖,從而直接將下一個(gè)操作進(jìn)行鏈?zhǔn)竭B接秽荤。
而使用also(即返回this的作用域函數(shù))可以將多個(gè)對(duì)同一對(duì)象的操作進(jìn)行鏈?zhǔn)秸{(diào)用,如下所示柠横。

original.also {
    println("The original String is $it") // "abc"
    it.reversed() // 即使我們改變它窃款,也是沒(méi)用的
}.also {
    println("The reverse String is ${it}") // "abc"
    it.length  // 即使我們改變它,也是沒(méi)用的
}.also {
    println("The length of the String is ${it}") // "abc"
}

這里只是為了演示牍氛,所以將可以寫(xiě)在同一個(gè)作用域函數(shù)中的進(jìn)行了拆分晨继。

also和let的鏈?zhǔn)秸{(diào)用,實(shí)際上各有不同的使用技巧搬俊,通過(guò)let紊扬,可以改變返回值,而通過(guò)also悠抹,可以將多個(gè)不同的原子操作通過(guò)鏈?zhǔn)竭M(jìn)行組合珠月,讓邏輯更加明朗。

國(guó)際慣例

also & apply

雖然also和apply都是返回this楔敌,但國(guó)際慣例,它們?cè)谑褂玫臅r(shí)候驻谆,還是有一些細(xì)微的差別的卵凑,also強(qiáng)調(diào)的是【與調(diào)用者無(wú)關(guān)的操作】,而apply強(qiáng)調(diào)的是【調(diào)用者的相關(guān)操作】胜臊,例如下面的這個(gè)例子勺卢。

test?.also {
    println("some log")
}?.apply {
    name = "xys"
}

let & run

let和run的返回值相同,它們的區(qū)別主要在于作用域內(nèi)使用it和this的區(qū)別象对。一般來(lái)說(shuō)黑忱,如果調(diào)用者的屬性和類中的屬性同名,則一般會(huì)使用let勒魔,避免出現(xiàn)同名的賦值引起混亂甫煞。

國(guó)際慣例,run通常使用在鏈?zhǔn)秸{(diào)用中冠绢,進(jìn)行數(shù)據(jù)處理抚吠、類型轉(zhuǎn)換,例如?.run{}的使用弟胀。

歡迎大家關(guān)注我的微信公眾號(hào)——Android群英傳

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末楷力,一起剝皮案震驚了整個(gè)濱河市喊式,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萧朝,老刑警劉巖岔留,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異检柬,居然都是意外死亡贸诚,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)厕吉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)酱固,“玉大人,你說(shuō)我怎么就攤上這事头朱≡吮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵项钮,是天一觀的道長(zhǎng)班眯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)烁巫,這世上最難降的妖魔是什么署隘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮亚隙,結(jié)果婚禮上磁餐,老公的妹妹穿的比我還像新娘。我一直安慰自己阿弃,他們只是感情好诊霹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著渣淳,像睡著了一般脾还。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上入愧,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天鄙漏,我揣著相機(jī)與錄音,去河邊找鬼棺蛛。 笑死怔蚌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鞠值。 我是一名探鬼主播媚创,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼彤恶!你這毒婦竟也來(lái)了钞钙?” 一聲冷哼從身側(cè)響起鳄橘,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芒炼,沒(méi)想到半個(gè)月后瘫怜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡本刽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年鲸湃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片子寓。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暗挑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斜友,到底是詐尸還是另有隱情炸裆,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布鲜屏,位于F島的核電站烹看,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏洛史。R本人自食惡果不足惜惯殊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望也殖。 院中可真熱鬧土思,春花似錦、人聲如沸毕源。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)霎褐。三九已至,卻和暖如春该镣,著一層夾襖步出監(jiān)牢的瞬間冻璃,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工损合, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留省艳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓嫁审,卻偏偏與公主長(zhǎng)得像跋炕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子律适,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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

  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔辐烂,今天18年5月份再次想寫(xiě)文章遏插,發(fā)現(xiàn)簡(jiǎn)書(shū)還為我保存起的...
    Jenaral閱讀 2,732評(píng)論 2 9
  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,744評(píng)論 0 38
  • 第一章 錯(cuò)誤處理: 錯(cuò)誤: 程序運(yùn)行過(guò)程中,導(dǎo)致程序無(wú)法正常執(zhí)行的現(xiàn)象(即bug) 現(xiàn)象: 程序一旦出錯(cuò)纠修,默認(rèn)會(huì)報(bào)...
    fastwe閱讀 1,097評(píng)論 0 1
  • 前言 最近使用kotlin語(yǔ)言開(kāi)發(fā)了新的項(xiàng)目胳嘲,kotlin的一些特性和大量的語(yǔ)法糖相當(dāng)好用,相比于java扣草,開(kāi)發(fā)效...
    SirWwh閱讀 2,346評(píng)論 1 2
  • 什么是作用域函數(shù)(Scope Functions)了牛? Kotlin 標(biāo)準(zhǔn)庫(kù)包含了幾個(gè)特殊的函數(shù),其目的是在調(diào)用對(duì)象...
    SkyRiN閱讀 912評(píng)論 1 4