kotlin的語(yǔ)法糖(操作符)??

認(rèn)識(shí)kotlin中的let、with强经、run睡陪、also、apply匿情、map兰迫、flatMap等操作符。

從java轉(zhuǎn)到kotlin遇到的第一個(gè)障礙就是kotlin自帶的操作符炬称,在看別人代碼的時(shí)候總是被各種各樣的操作符弄的一頭霧水汁果。為什么他可以這么寫(xiě)?為什么他可以直接使用對(duì)象的屬性玲躯?這一系列的代碼執(zhí)行之后到底變成了什么樣子据德?伴隨著各種各樣的問(wèn)題,我們不得不先學(xué)習(xí)一下kotlin的操作符跷车。

本文對(duì)kotlin中常用的操作符進(jìn)行舉例說(shuō)明棘利,方便開(kāi)發(fā)者理解使用。

1. 基礎(chǔ)操作符

1.1 let

將調(diào)用者傳入代碼塊中姓赤,以it指代傳入的對(duì)象,執(zhí)行代碼塊的代碼仲吏,將代碼塊最后一行結(jié)果或return指定的數(shù)據(jù)返回不铆。

let的執(zhí)行效果和把代碼寫(xiě)在代碼塊外面差不多蝌焚,主要的作用是可以對(duì)test變量是否為空做出判斷,如果test為空則不會(huì)執(zhí)行代碼塊中的代碼誓斥。對(duì)于代碼的閱讀性有一定的提升庵寞,業(yè)務(wù)邏輯和臨時(shí)變量都寫(xiě)在代碼塊中齐疙,方便區(qū)分。

test1("test1")

private fun test1(input: String?) {
    // 返回一個(gè)字符串中的第一個(gè)數(shù)字字符所對(duì)應(yīng)的數(shù)字,找不到則返回null
    val result = input?.let { // 如果input為空拦英,則不會(huì)執(zhí)行l(wèi)et代碼塊的代碼,直接返回null
        var number: Int? = null
        for (i in it.iterator()) { // 調(diào)用傳入對(duì)象的方法需要使用it引用
            if (Character.isDigit(i)) {
                number = Integer.parseInt(i.toString())
                break
            }
        }
        number
    }
    LogUtil.print("result = $result") // result = 1
}

1.2 with

傳入一個(gè)對(duì)象悬槽,在對(duì)象內(nèi)部執(zhí)行代碼塊中的代碼穗酥,可以直接調(diào)用傳入對(duì)象的公共方法及屬性,也可以使用this指代傳入對(duì)象進(jìn)行操作框仔,將代碼塊最后一行結(jié)果或return指定的數(shù)據(jù)返回舀武。

with操作符不好用,他無(wú)法以鏈?zhǔn)秸{(diào)用的方式承接上面的數(shù)據(jù)离斩,如果傳入的對(duì)象可能為空银舱,在使用的時(shí)候依舊需要對(duì)空指針進(jìn)行判斷。一般在需要重復(fù)多次調(diào)用同一個(gè)對(duì)象時(shí)可以使用這個(gè)操作符跛梗,可以省去調(diào)用對(duì)象的名稱(chēng)寻馏。

test2("test2")

private fun test2(input: String?) {
    // 返回一個(gè)字符串中的第一個(gè)數(shù)字字符所對(duì)應(yīng)的數(shù)字,找不到則返回null
    val result = with(input) {
        if (this == null) {
            return@with null
        }
        var number: Int? = null
        for (i in iterator()) { // 此處可以直接調(diào)用String.iterator()方法
            if (Character.isDigit(i)) {
                number = Integer.parseInt(i.toString())
                break
            }
        }
        number
    }
    LogUtil.print("result = $result") // result = 2
}

1.3 run

將調(diào)用者傳入代碼塊中核偿,在調(diào)用者內(nèi)部執(zhí)行代碼诚欠,可以直接調(diào)用傳入對(duì)象的公共方法及屬性,也可以使用this指代傳入對(duì)象進(jìn)行操作宪祥,將代碼塊最后一行結(jié)果或return指定的數(shù)據(jù)返回聂薪。

run操作符是letwith的結(jié)合體,將他們的優(yōu)點(diǎn)集中到一起蝗羊,既可以插入到鏈?zhǔn)秸{(diào)用中藏澳,又能直接在代碼塊中調(diào)用傳入對(duì)象的公共方法及屬性,而且在調(diào)用前進(jìn)行空指針判斷也很方便耀找。

test3("test3")

private fun test3(input: String?) {
    // 返回一個(gè)字符串中的第一個(gè)數(shù)字字符所對(duì)應(yīng)的數(shù)字翔悠,找不到則返回null
    val result = input?.run { // 如果input為空,則不會(huì)執(zhí)行run代碼塊的代碼野芒,直接返回null
        var number: Int? = null
        for (i in iterator()) { // 此處可以直接調(diào)用String.iterator()方法
            if (Character.isDigit(i)) {
                number = Integer.parseInt(i.toString())
                break
            }
        }
        number
    }
    LogUtil.print("result = $result") // result = 3
}

1.4 also

將調(diào)用者傳入代碼塊中蓄愁,以it指代傳入的對(duì)象,執(zhí)行代碼塊的代碼狞悲,代碼執(zhí)行完成后將調(diào)用對(duì)象返回撮抓。

alsolet的使用方法和執(zhí)行效果差不多,唯一的區(qū)別是also返回的是調(diào)用者本身摇锋。

test4("test4")

private fun test4(input: String?) {
    // 創(chuàng)建一個(gè)內(nèi)容為輸入字符串丹拯,字號(hào)為20sp站超,顏色為白色的TextView
    val textView = TextView(this)
    val result = textView.also { // 此處可以直接將also連接在構(gòu)造函數(shù)后,能夠減少一個(gè)臨時(shí)變量
        it.text = input ?: ""
        it.textSize = 20f
        it.setTextColor(0xFFFFFFFF.toInt()) // 最終的返回值為調(diào)用者乖酬,并不是最后一行代碼的值
    }
    LogUtil.print("result = ${result.text}") // result = test4
}

1.5 apply

將調(diào)用者傳入代碼塊中死相,在調(diào)用者內(nèi)部執(zhí)行代碼,可以直接調(diào)用傳入對(duì)象的公共方法及屬性咬像,也可以使用this指代傳入對(duì)象進(jìn)行操作算撮,執(zhí)行代碼塊的代碼,代碼執(zhí)行完成后將調(diào)用對(duì)象返回县昂。

apply是對(duì)also的升級(jí)肮柜,調(diào)用方法和屬性時(shí)不用再使用it調(diào)用。也可以看作是run的變種七芭,使用方法和run一致素挽,最終返回傳入的對(duì)象。apply常用于設(shè)置一個(gè)對(duì)象的多個(gè)屬性狸驳,對(duì)于不支持鏈?zhǔn)秸{(diào)用的對(duì)象预明,可以提供一個(gè)類(lèi)似鏈?zhǔn)秸{(diào)用的效果。

test5("test5")

private fun test5(input: String?) {
    // 創(chuàng)建一個(gè)內(nèi)容為輸入字符串耙箍,字號(hào)為20sp撰糠,顏色為白色的TextView
    val textView = TextView(this)
    val result = textView.apply { // 此處可以直接將apply連接在構(gòu)造函數(shù)后,能夠減少一個(gè)臨時(shí)變量
        text = input ?: ""
        textSize = 20f
        setTextColor(0xFFFFFFFF.toInt()) // 最終的返回值為調(diào)用者辩昆,并不以最后一行代碼的值為準(zhǔn)
    }
    LogUtil.print("result = ${result.text}") // result = test5
}

1.6 forEach & forEachIndexed

遍歷一個(gè)列表阅酪,對(duì)實(shí)現(xiàn)Iterable接口的對(duì)象進(jìn)行遍歷,將列表中的每一個(gè)數(shù)據(jù)提取出來(lái)傳遞到代碼塊中汁针,forEach會(huì)將數(shù)據(jù)用it指定并傳入代碼塊中术辐,forEachIndexed則會(huì)多傳遞一個(gè)index,用于標(biāo)記當(dāng)前數(shù)據(jù)的位置施无。

forEach和forEachIndexed并不會(huì)返回任何數(shù)據(jù)

val list = listOf(1, 2, 3, 4, 5)
list.forEach {
    LogUtil.print(it)
}

list.forEachIndexed { index, i ->
    LogUtil.print("$index - $i")
}

1.7 小結(jié)

  • 以上“在對(duì)象內(nèi)部執(zhí)行代碼”的說(shuō)法是方便開(kāi)發(fā)者理解辉词,實(shí)際的執(zhí)行位置并不在對(duì)象內(nèi)部,所以只能調(diào)用對(duì)象的公共方法及屬性猾骡,但代碼書(shū)寫(xiě)方式卻和在對(duì)象內(nèi)部書(shū)寫(xiě)私有方法一樣瑞躺。
  • letrun兴想、apply幢哨、also操作符直接寫(xiě)在函數(shù)中時(shí),調(diào)用者為函數(shù)所在對(duì)象嫂便。
  • let捞镰、withrun均是以閉包形式執(zhí)行,返回的數(shù)據(jù)為return數(shù)據(jù)或最后一行代碼的值岸售。
  • apply几迄、also的返回值均是調(diào)用者自身。
  • 一般情況下使用runapply就足以滿足業(yè)務(wù)需求冰评,其他三個(gè)操作符了解運(yùn)行效果,能夠讀懂別人的代碼即可木羹。

2. 流程操作符

以上的基礎(chǔ)操作符也可用于流程中的數(shù)據(jù)處理甲雅。
在kotlin之前使用過(guò)RxJava,kotlin的流程操作符和RxJava差不多坑填,在開(kāi)發(fā)過(guò)程中可以直接使用kotlin內(nèi)置的操作符而不需要再引入第三方庫(kù)了抛人。

2.1 map

一對(duì)一的轉(zhuǎn)換,將n個(gè)數(shù)據(jù)的列表轉(zhuǎn)換成n個(gè)數(shù)據(jù)的列表脐瑰,類(lèi)型及數(shù)據(jù)都可以變換妖枚。僅適用于列表或可以轉(zhuǎn)換成列表的數(shù)據(jù),準(zhǔn)確的說(shuō)是實(shí)現(xiàn)了kotlin.collections.Iterable<T>接口的對(duì)象(例如:String會(huì)轉(zhuǎn)換成List<Char>進(jìn)行處理)苍在。map操作符會(huì)把列表中的每一個(gè)數(shù)據(jù)提取出來(lái)绝页,用it指定,然后執(zhí)行代碼塊中的代碼寂恬,返回return指定的數(shù)據(jù)或最后一行代碼的值续誉。

當(dāng)我們需要依次處理一個(gè)列表中的每個(gè)數(shù)據(jù)的時(shí)候就可以使用map操作符,相當(dāng)于java的for-each循環(huán)初肉。和RxJava中的map效果一樣酷鸦。這個(gè)流程對(duì)數(shù)據(jù)的數(shù)量不會(huì)有影響。

val inputList = listOf(5, 4, 3, 2, 1) //創(chuàng)建一個(gè)包含5個(gè)數(shù)字的列表牙咏,類(lèi)型為L(zhǎng)ist<Int>
val result = inputList.map { // it指代當(dāng)前處理的數(shù)據(jù)
    if (it == 1) {
        return@map "first"
    }
    "index_$it"
}
LogUtil.print("result = $result") // result = [index_5, index_4, index_3, index_2, first]

示例中輸入的是5個(gè)int數(shù)字臼隔,我們通過(guò)判斷將值為1的數(shù)字修改為“first”,其余數(shù)字則添加“index_”前綴妄壶,最終輸出的是一個(gè)字符串?dāng)?shù)組摔握。建議返回同樣類(lèi)型的數(shù)據(jù),這樣后續(xù)繼續(xù)處理也會(huì)方便一些盯拱,如果返回的數(shù)據(jù)類(lèi)型不一致盒发,得到的列表類(lèi)型會(huì)是Any,不方便繼續(xù)處理數(shù)據(jù)狡逢。

2.2 flatMap

一對(duì)多的轉(zhuǎn)換宁舰,將n個(gè)數(shù)據(jù)的列表根據(jù)處理邏輯轉(zhuǎn)換成m個(gè)數(shù)據(jù)的列表,類(lèi)型及數(shù)據(jù)都可以變換奢浑。使用要求和方式與map一樣蛮艰,但代碼塊中返回的結(jié)果要求是一個(gè)列表。最終的結(jié)果是將所有返回列表的數(shù)據(jù)連到一起雀彼,組成一個(gè)新列表壤蚜。

flatMap對(duì)返回列表的數(shù)據(jù)個(gè)數(shù)不做限制即寡,我們可以通過(guò)flatMap操作符調(diào)整列表中數(shù)據(jù)的個(gè)數(shù),也可以將細(xì)分的數(shù)據(jù)提到上層處理袜刷。當(dāng)我們需要把一些對(duì)象中的子數(shù)據(jù)提取到一個(gè)列表中時(shí)聪富,使用flatMap就很方便。

val inputList = listOf(5, 4, 3, 2, 1) //創(chuàng)建一個(gè)包含5個(gè)數(shù)字的列表著蟹,類(lèi)型為L(zhǎng)ist<Int>
val result = inputList.flatMap {
    if (it <= 1) {
        return@flatMap listOf("$it")
    }
    val index: MutableList<String> = mutableListOf()
    for (i in 1..it) { // 這里把傳入的數(shù)據(jù)當(dāng)作循環(huán)次數(shù)使用墩蔓,如果傳入數(shù)據(jù)是個(gè)數(shù)據(jù)模型,也可以直接提取其中的列表數(shù)據(jù)萧豆。
        index.add("$it-$i")
    }
    index
}
LogUtil.print("result = $result")
// result = [5-1, 5-2, 5-3, 5-4, 5-5, 4-1, 4-2, 4-3, 4-4, 3-1, 3-2, 3-3, 2-1, 2-2, 1]

示例中的MutableList是一個(gè)可變列表奸披,kotlin中分為可變列表和不可變列表,當(dāng)需要?jiǎng)討B(tài)修改列表數(shù)據(jù)個(gè)數(shù)的時(shí)候就要使用可變列表涮雷。我們通過(guò)flatMap操作符對(duì)原始列表進(jìn)行展開(kāi)處理阵面,最終的結(jié)果是將我們每次返回的列表整合成一個(gè)新的列表。

2.3 use

可以自動(dòng)關(guān)閉使用的資源洪鸭,針對(duì)的是實(shí)現(xiàn)了Closeable接口的數(shù)據(jù)样刷。use操作符會(huì)把使用的對(duì)象傳遞到代碼塊中,用it指定览爵,然后執(zhí)行代碼塊中的代碼颂斜,返回return指定的數(shù)據(jù)或最后一行代碼的值。

使用這個(gè)操作符可以代替?zhèn)鹘y(tǒng)的try-catch-finally代碼塊拾枣,而且不會(huì)影響流式代碼的結(jié)構(gòu)沃疮。每次使用需要手動(dòng)關(guān)閉的對(duì)象時(shí)就可以使用use操作符簡(jiǎn)化代碼了。

BufferedReader(InputStreamReader(FileInputStream(File("a.txt")), Charsets.UTF_8)).use {
    val content = it.readLine()
    LogUtil.print(content)
}

這個(gè)示例展示了一個(gè)按行讀取文件的效果梅肤,先后構(gòu)建了File司蔬、FileInputStreamInputStreamReader姨蝴、BufferedReader俊啼,最終通過(guò)use操作符自動(dòng)關(guān)閉了所有的資源。(BufferedReader在關(guān)閉的時(shí)候會(huì)自動(dòng)關(guān)閉引用的InputStreamReader左医,所以對(duì)最外層的BufferedReader使用use即可)授帕。

對(duì)于按行讀取文件的功能,kotlin已經(jīng)提供了相應(yīng)的擴(kuò)展方法浮梢,該方法也是基于use實(shí)現(xiàn)的自動(dòng)關(guān)閉功能跛十,直接調(diào)用該方法即可。

File("a.txt").readLines().forEach {
    LogUtil.print(it)
}

3. 總結(jié)

kotlin的各種操作符基本上都是通過(guò)擴(kuò)展方法和內(nèi)聯(lián)函數(shù)實(shí)現(xiàn)的秕硝,這些操作符都是為了方便代碼開(kāi)發(fā)而添加的芥映,隨著kotlin越來(lái)越成熟,方便開(kāi)發(fā)人員使用的操作符也會(huì)越來(lái)越多,單純靠總結(jié)現(xiàn)有的操作符是無(wú)法全部掌握的奈偏,如果遇到不了解的操作符坞嘀,可以進(jìn)入操作符的方法中,查看一下源碼的實(shí)現(xiàn)方式惊来,再配合注釋就可以輕松使用大部分操作符了丽涩。

參考文章

Kotlin系列之let、with裁蚁、run内狸、apply、also函數(shù)的使用@熊喵先生

結(jié)尾


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厘擂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锰瘸,更是在濱河造成了極大的恐慌刽严,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件避凝,死亡現(xiàn)場(chǎng)離奇詭異舞萄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)管削,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)倒脓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人含思,你說(shuō)我怎么就攤上這事崎弃。” “怎么了含潘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵饲做,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我遏弱,道長(zhǎng)盆均,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任漱逸,我火速辦了婚禮泪姨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饰抒。我一直安慰自己肮砾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布袋坑。 她就那樣靜靜地躺著唇敞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疆柔,一...
    開(kāi)封第一講書(shū)人閱讀 52,785評(píng)論 1 314
  • 那天咒精,我揣著相機(jī)與錄音,去河邊找鬼旷档。 笑死模叙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鞋屈。 我是一名探鬼主播范咨,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼厂庇!你這毒婦竟也來(lái)了渠啊?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤权旷,失蹤者是張志新(化名)和其女友劉穎替蛉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拄氯,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躲查,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了译柏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镣煮。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鄙麦,靈堂內(nèi)的尸體忽然破棺而出典唇,到底是詐尸還是另有隱情,我是刑警寧澤胯府,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布蚓聘,位于F島的核電站,受9級(jí)特大地震影響盟劫,放射性物質(zhì)發(fā)生泄漏夜牡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一侣签、第九天 我趴在偏房一處隱蔽的房頂上張望塘装。 院中可真熱鬧,春花似錦影所、人聲如沸蹦肴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)阴幌。三九已至勺阐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間矛双,已是汗流浹背渊抽。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留议忽,地道東北人懒闷。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像栈幸,于是被迫代替她去往敵國(guó)和親愤估。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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