Kotlin 中 let、apply、run、with及 also 的差別

let

public inline fun <T, R> T.let(block: (T) -> R): R

let 函數(shù)是參數(shù)化類型 T 的擴(kuò)展函數(shù)。在 let 塊內(nèi)可以通過 it 指代該對(duì)象。返回值為 let 塊的最后一行或指定 return 表達(dá)式。

以一個(gè) Book 對(duì)象為例,類中包含 Book 的 name 和 price,如下:

class Book {
    var name = "《數(shù)據(jù)結(jié)構(gòu)》"
    var price = 60
    fun displayInfo() = println("Book name : $name and price : $price")
}
  • let 塊中的最后一條語句如果是非賦值語句,則默認(rèn)情況下它是返回語句,反之,則返回的是一個(gè) Unit 類型。

控制臺(tái)輸出:
This book is 《計(jì)算機(jī)網(wǎng)絡(luò)》

對(duì) Book 對(duì)象使用 let 作用域函數(shù),在函數(shù)塊的最后一句添加了一行字符串代碼,并且對(duì) Book 對(duì)象進(jìn)行打印,我們可以看到最后控制臺(tái)輸出的結(jié)果為字符串 “This book is 《計(jì)算機(jī)網(wǎng)絡(luò)》”。

在 Kotlin 中,如果 let 塊中的最后一條語句是非賦值語句,則默認(rèn)情況下它是返回語句。

那如果我們將 let 塊中最后一條語句修改為賦值語句,會(huì)發(fā)生什么變化?

fun main() {
    val book = Book().let {
        it.name = "《計(jì)算機(jī)網(wǎng)絡(luò)》"
    }
    println(book)
}

控制臺(tái)輸出:
kotlin.Unit

將 Book 對(duì)象的 name 值進(jìn)行了賦值操作,同樣對(duì) Book 對(duì)象進(jìn)行打印,但是最后控制臺(tái)的輸出結(jié)果為“kotlin.Unit”,這是因?yàn)樵?let 函數(shù)塊的最后一句是賦值語句,print 則將其當(dāng)做是一個(gè)函數(shù)來看待。

  • let 可用于空安全檢查。

如需對(duì)非空對(duì)象執(zhí)行操作,可對(duì)其使用安全調(diào)用操作符 ?.并調(diào)用 let 在 lambda 表達(dá)式中執(zhí)行操作。如下案例:

var name: String? = null
fun main() {
    val nameLength = name?.let {
        it.length
    } ?: "name為空時(shí)的值"
    println(nameLength)
}

控制臺(tái)輸出:
name為空時(shí)的值

設(shè)置 name 為一個(gè)可空字符串,利用 name?.let 來進(jìn)行空判斷,只有當(dāng) name 不為空時(shí),邏輯才能走進(jìn) let 函數(shù)塊中。在這里,我們可能還看不出來 let 空判斷的優(yōu)勢(shì),但是當(dāng)你有大量 name 的屬性需要編寫的時(shí)候,就能發(fā)現(xiàn) let 的快速和簡(jiǎn)潔。

  • let 可對(duì)調(diào)用鏈的結(jié)果進(jìn)行操作。
fun main() {
    val numbers = mutableListOf("One", "Two", "Three", "Four", "Five")
    val resultsList: List<Int> = numbers.map {
        it.length
    }.filter {
        it > 3
    }
    println(resultsList)
}

控制臺(tái)輸出:
[5, 4, 4]

目的是獲取數(shù)組列表中長(zhǎng)度大于 3 的值。因?yàn)槲覀儽仨毚蛴〗Y(jié)果,所以我們將結(jié)果存儲(chǔ)在一個(gè)單獨(dú)的變量中,然后打印它。但是使用“l(fā)et”操作符,我們可以將代碼修改為:

fun main() {
    val numbers = mutableListOf("One", "Two", "Three", "Four", "Five")
    numbers.map {
        it.length
    }.filter {
        it > 3
    }.let {
        print(it)
    }
}

控制臺(tái)輸出:
[5, 4, 4]

使用 let 后可以直接對(duì)數(shù)組列表中長(zhǎng)度大于 3 的值進(jìn)行打印,去掉了變量賦值這一步。

  • let 可以將 “It” 重命名為一個(gè)可讀的 lambda 參數(shù)。

let 是通過使用“It”關(guān)鍵字來引用對(duì)象的上下文,因此,這個(gè) “It” 可以被重命名為一個(gè)可讀的 lambda 參數(shù),如下將 it 重命名為 book:

fun main() {
    val book = Book().let { book ->
        book.name = "《計(jì)算機(jī)網(wǎng)絡(luò)》"
    }
    print(book)
}

控制臺(tái)輸出:
kotlin.Unit

apply

public inline fun <T> T.apply(block: T.() -> Unit): T

apply 是 T 的擴(kuò)展函數(shù),與 run 函數(shù)有些相似,它將對(duì)象的上下文引用為 “this” 而不是 “it”,并且提供空安全檢查,不同的是,apply 不接受函數(shù)塊中的返回值,返回的是自己的 T 類型對(duì)象。

控制臺(tái)輸出:
Book@7c30a502

apply 函數(shù)返回傳入的對(duì)象的本身

在 let 中,沒有在函數(shù)塊中返回的值,最終會(huì)成為 Unit 類型,但在 apply 中,最后返回對(duì)象本身 (T) 時(shí),它成為 Book 類型。

apply 函數(shù)主要用于初始化或更改對(duì)象,因?yàn)樗糜谠诓皇褂脤?duì)象的函數(shù)的情況下返回自身。

run

run 函數(shù)以 “this”作為上下文對(duì)象。

fun main() {
    val book = Book().run {
        // this ==  Book() 本身
        name = "《計(jì)算機(jī)網(wǎng)絡(luò)》"
        price = 40
        displayInfo()
        555  // 返回類型,根據(jù)匿名函數(shù)最后一行的變化而變化
    }
    // 返回值 = 函數(shù)塊的最后一行 / return表達(dá)式
    println(book)
}

控制臺(tái)輸出:
Book name : 《計(jì)算機(jī)網(wǎng)絡(luò)》 and price : 40
555

run 函數(shù)存在兩種聲明方式。
1、與 let 一樣,run 是作為 T 的擴(kuò)展函數(shù)。

public inline fun <T, R> T.run(block: T.() -> R): R

2、第二個(gè) run 的聲明方式則不同,它不是擴(kuò)展函數(shù),并且塊中也沒有輸入值,因此,它不是用于傳遞對(duì)象并更改屬性的類型,而是可以使你在需要表達(dá)式的地方就可以執(zhí)行一個(gè)語句

public inline fun <R> run(block: () -> R): R 

如下利用 run 函數(shù)塊執(zhí)行方法,而不是作為一個(gè)擴(kuò)展函數(shù)。

run {
        val book = Book()
        book.name = "《計(jì)算機(jī)網(wǎng)絡(luò)》"
        book.price = 30
        book.displayInfo()
    }
// 具名函數(shù)調(diào)用給 run 執(zhí)行
// str.run(具名函數(shù))
val str = "《計(jì)算機(jī)網(wǎng)絡(luò)》"
str.run(::getStr)

fun getStr(str: String) {
    println(str.length)
}
// 匿名函數(shù)調(diào)用給 run 執(zhí)行
str.run {
    // this == str 本身
    if (length > 3) true else false
}

with

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

with 屬于非擴(kuò)展函數(shù),直接輸入一個(gè)對(duì)象 receiver,當(dāng)輸入 receiver 后,便可以更改 receiver 的屬性,同時(shí),它也與 run 做著同樣的事情。

fun main() {
    val book = Book()

    val str: String = with(book) {
        name = "《計(jì)算機(jī)網(wǎng)絡(luò)》"
        price = 40
        displayInfo()
        "dfdggg" // 返回值 = 函數(shù)塊的最后一行 / return表達(dá)式
    }
    print(str)
}

控制臺(tái)輸出:
Book name : 《計(jì)算機(jī)網(wǎng)絡(luò)》 and price : 40
dfdggg

with(T)類型傳入了一個(gè)參數(shù) book,則可以在 with 的代碼塊中訪問 book 的 name 和 price 屬性,并做更改。

fun main() {
   /*with(str){
       // this == str 本身
    }*/
    // 具名操作
   val str = "《計(jì)算機(jī)網(wǎng)絡(luò)》"
    with(str, ::println)
    val len: Int = with(str, ::getStrLen)
    println(len)

    // 匿名操作
    with(str) {
        // this == str 本身
        length
    }
}

fun getStrLen(str: String) = str.length

with 使用的是非 null 的對(duì)象,當(dāng)函數(shù)塊中不需要返回值時(shí),可以使用 with。

also

public inline fun <T> T.also(block: (T) -> Unit): T 

also 是 T 的擴(kuò)展函數(shù),返回值與 apply 一致,直接返回 T。also 函數(shù)的用法類似于 let 函數(shù),將對(duì)象的上下文引用為 “it” 而不是 “this” 以及提供空安全檢查方面。

作用 & 應(yīng)用場(chǎng)景

類似 let 函數(shù),但區(qū)別在于返回值:

  • let函數(shù):返回值 = 最后一行 / return的表達(dá)式
  • also函數(shù):返回值 = 傳入的對(duì)象的本身
fun main() {
    val str = "abcdefg"
    str.also {
        // also 函數(shù)始終返回 str 本身
        println("str的原始數(shù)據(jù):$it")
    }
}
// let函數(shù)
var result = mVar.let {
               it.function1()
               it.function2()
               it.function3()
               999
}
// 最終結(jié)果 = 返回999給變量result

// also函數(shù)
var result = mVar.also {
               it.function1()
               it.function2()
               it.function3()
               999
}
// 最終結(jié)果 = 返回一個(gè)mVar對(duì)象給變量result

總結(jié)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容