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é)
