Kotlin中標(biāo)準(zhǔn)函數(shù)run、with、let、also與apply的使用和區(qū)別詳解
前言
和Java相比,在Kotlin中提供了不少新的特性。這次我們就來聊一聊Kotlin的一些通用的擴(kuò)展標(biāo)準(zhǔn)函數(shù)run,with,let,also和apply。對于這五個(gè)標(biāo)準(zhǔn)函數(shù)它們都存在于Kotlin的源碼標(biāo)準(zhǔn)庫當(dāng)中,也就是在Standard.kt文件當(dāng)中。它們都是適用于任何對象的通用擴(kuò)展函數(shù)。但是對于run,with,let,also和apply這五個(gè)函數(shù)他們的用法及其相似,以至于我們無法確定去選擇使用哪一個(gè)。那么現(xiàn)在我們就來聊一下這五個(gè)函數(shù)它們的使用方法,它們的不同之處以及在什么場景下去使用。
作用域函數(shù)
在這里我們重點(diǎn)是看一下run,with,T.run,T.let,T.also,和T.apply,對于這幾個(gè)函數(shù)來說它們最重要的功能之一是在調(diào)用函數(shù)的內(nèi)部又提供了一個(gè)作用域。
那么下面就通過一段代碼來看一下run函數(shù)的作用域,對于其它函數(shù)來說當(dāng)然也是類似。
fun test(){
var animal = "cat"
run {
val animal = "dog"
println(animal) // dog
}
println(animal) //cat
}
在這個(gè)簡單的test函數(shù)當(dāng)中我們擁有一個(gè)單獨(dú)的作用域,在run函數(shù)中能夠重新定義一個(gè)animal變量,并且它的作用域只存在于run函數(shù)當(dāng)中。
目前對于這個(gè)run函數(shù)看起來貌似沒有什么用處,但是在run函數(shù)當(dāng)中它不僅僅只是一個(gè)作用域,他還有一個(gè)返回值。他會(huì)返回在這個(gè)作用域當(dāng)中的最后一個(gè)對象。
例如現(xiàn)在有這么一個(gè)場景,用戶領(lǐng)取app的獎(jiǎng)勵(lì),如果用戶沒有登錄彈出登錄dialog,如果已經(jīng)登錄則彈出領(lǐng)取獎(jiǎng)勵(lì)的dialog。我們可以使用以下代碼來處理這個(gè)邏輯。
run {
if (islogin) loginDialog else getAwardDialog
}.show()
可以看到上面這段代碼會(huì)變得更加的簡潔,并且可以將show方法一次應(yīng)用到上面兩個(gè)dialog當(dāng)中,而不是去調(diào)用兩次。
with和其它通用標(biāo)準(zhǔn)函數(shù)
在這里之所以將with函數(shù)單獨(dú)拿出來進(jìn)行說明,是因?yàn)閣ith得用法和其它通用的標(biāo)準(zhǔn)函數(shù)的用法比較獨(dú)特。在這里我們依然使用run函數(shù)來進(jìn)行對比。對于下面這段代碼做的是同樣一件事。它們的不同之處就是一個(gè)使用了with(T)函數(shù),而另一個(gè)則是使用了T.run函數(shù)。
with(webView.settings){
javaScriptEnabled = true
databaseEnabled = true
}
webView.settings.run {
javaScriptEnabled = true
databaseEnabled = true
}
但是我們覺得使用哪一個(gè)會(huì)更好呢?現(xiàn)在假設(shè)一種場景,那就是webView.settings可能為null。那我們就來再次看一下下面這段代碼.
with(webView.settings){
javaScriptEnabled = true
databaseEnabled = true
}
webView.settings?.run {
javaScriptEnabled = true
databaseEnabled = true
}
這么以來就很明顯了,當(dāng)然是T.run方法會(huì)更好,因?yàn)槲覀兛梢栽谑褂眠@些函數(shù)之前可以進(jìn)行對null的檢查。
對于with也是存在一個(gè)返回值,它也是會(huì)返回在這個(gè)作用域當(dāng)中的最后一個(gè)對象。
作用域中接收者this和it
在這幾個(gè)擴(kuò)展函數(shù)當(dāng)中,它們都能直接獲取到調(diào)用的對象或者是with中傳入?yún)?shù)的對象。在這五個(gè)擴(kuò)展函數(shù)在它們的作用域中的接收者可以是this或者是it。那么我們來對比一下T.run和T.let函數(shù)。這兩個(gè)函數(shù)也是十分的相似。
stringVariable?.run {
println("字符串的長度為$length")
}
stringVariable?.let {
println("字符串的長度為 ${it.length}")
}
在這兩段代碼中可以清晰的看到。在T.run函數(shù)中通過this來獲取stringVariable對象,而在T.let函數(shù)中通過it來取出stringVariable對象。當(dāng)然我們也能夠?yàn)閕t重新命名。如果我們不想覆蓋外部作用域的this,這時(shí)候去使用T.let會(huì)更加的方便。至于哪些函數(shù)的接收者是this,哪些函數(shù)的接收者是it,在后面會(huì)通過一張樹狀圖清晰的體現(xiàn)出來。
在作用域中返回值的類型
在這些作用域中它們都會(huì)存在一個(gè)返回值。在上面的講述的run,with,T.run,T.let中它們返回的都是作用域中最后一個(gè)對象。當(dāng)然它們所返回的值是允許和接受者it或者this對象的類型不同。但是并不是所有的標(biāo)準(zhǔn)函數(shù)都是返回作用域的最后一個(gè)對象。例如T.also函數(shù)。
val original = "abc"
original.let {
println("The original String is $it") // "abc"
it.reversed()
}.let {
println("The reverse String is $it") // "cba"
it.length
}.let {
println("The length of the String is $it") // 3
}
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"
}
從上面兩段代碼可以看出T.let和T.also的返回值使不同的。T.let返回的是作用域中的最后一個(gè)對象,它的值和類型都可以改變。但是T.also不管調(diào)用多少次返回的都是原來的original對象。
對于T.let和T.also都能夠進(jìn)行鏈?zhǔn)讲僮鳎敲次覀儸F(xiàn)在結(jié)合一下T.let和T.also的鏈?zhǔn)秸{(diào)用來看一下在實(shí)際場景中的應(yīng)用。
//原始函數(shù)
fun makeDir(path: String): File {
val result = File(path)
result.mkdirs()
return result
}
//通過let和also的鏈?zhǔn)秸{(diào)用改進(jìn)后的函數(shù)
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }
擴(kuò)展函數(shù)的特性
到目前為止除了T.apply沒有使用到以外,根據(jù)上面的用法我們可以總結(jié)出來這些標(biāo)準(zhǔn)函數(shù)的三大特性。
- 它們都有自己的作用域
- 它們作用域中的接收者是this或者it
- 它們都有一個(gè)返回值,返回最后一個(gè)對象(this)或者調(diào)用者自身(itself)
由此可想到對于T.apply無非也就是這三個(gè)特性。對于T.apply它作用域中的接收者是this,并且返回的調(diào)用者T。因此,T.apply的其中一個(gè)使用場景可以用來創(chuàng)建一個(gè)Fragment,代碼如下所示:
// 使用普通的方法創(chuàng)建一個(gè)Fragment
fun createInstance(args: Bundle) : MyFragment {
val fragment = MyFragment()
fragment.arguments = args
return fragment
}
// 通過apply來改善原有的方法創(chuàng)建一個(gè)Fragment
fun createInstance(args: Bundle)
= MyFragment().apply { arguments = args }
我們也能夠通過T.apply的鏈?zhǔn)秸{(diào)用創(chuàng)建一個(gè)Intent:
// 普通創(chuàng)建Intent方法
fun createIntent(intentData: String, intentAction: String): Intent {
val intent = Intent()
intent.action = intentAction
intent.data=Uri.parse(intentData)
return intent
}
// 通過apply函數(shù)的鏈?zhǔn)秸{(diào)用創(chuàng)建Intent
fun createIntent(intentData: String, intentAction: String) =
Intent().apply { action = intentAction }
.apply { data = Uri.parse(intentData) }
如何選擇使用
在這里我們通過一個(gè)樹狀圖來看一下對著五個(gè)標(biāo)準(zhǔn)函數(shù)的區(qū)別,使用以及如何選取標(biāo)準(zhǔn)函數(shù)(圖片來源于參考文獻(xiàn)當(dāng)中)

總結(jié)
在這里做一下總結(jié),我們可以看出在這五個(gè)通用標(biāo)準(zhǔn)函數(shù)當(dāng)中它們的特性也是十分的簡單,無非也就是接收者和返回值的不同。對于with,T.run,T.apply接收者是this,而T.let和T.also接受者是it;對于with,T.run,T.let返回值是作用域的最后一個(gè)對象(this),而T.apply和T.also返回值是調(diào)用者本身(itself)。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Kotlin中l(wèi)et()with()run()apply()also()函數(shù)的使用方法與區(qū)別
在Kotlin中的源碼標(biāo)準(zhǔn)庫(Standard.kt)中提供了一些Kotlin擴(kuò)展的內(nèi)置函數(shù)可以優(yōu)化kotlin的編碼,今天為大家聊聊let,with,run,apply,also幾個(gè)函數(shù)的用法與區(qū)別2018-03-03
Android編程解析Json格式數(shù)據(jù)的方法
這篇文章主要介紹了Android編程解析Json格式數(shù)據(jù)的方法,涉及Android中json格式數(shù)據(jù)的構(gòu)造、讀取及遍歷等技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
RecyclerView實(shí)現(xiàn)仿支付寶應(yīng)用管理
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)仿支付寶應(yīng)用管理的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
Android自動(dòng)測試工具M(jìn)onkey
Monkey是Android中的一個(gè)命令行工具,可以運(yùn)行在模擬器里或?qū)嶋H設(shè)備中。它向系統(tǒng)發(fā)送偽隨機(jī)的用戶事件流(如按鍵輸入、觸摸屏輸入、手勢輸入等),實(shí)現(xiàn)對正在開發(fā)的應(yīng)用程序進(jìn)行壓力測試。Monkey測試是一種為了測試軟件的穩(wěn)定性、健壯性的快速有效的方法2016-01-01
Android studio4.1更新后出現(xiàn)的問題詳解
這篇文章主要介紹了Android studio4.1更新后出現(xiàn)的問題詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Android捕捉錯(cuò)誤try catch 的簡單使用教程
這篇文章主要介紹了Android捕捉錯(cuò)誤try catch 的簡單使用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Android簡單的利用MediaRecorder進(jìn)行錄音的實(shí)例代碼
MediaRecorder可以進(jìn)行簡單的錄音,由于操作簡單所以可以用來進(jìn)行基本的錄音。下面提供一個(gè)簡單的例子,記得在Mainfest文件中添加權(quán)限2013-08-08

