您是否常常看到 Kotlin 的程式碼中,有一些特殊的函式,如:apply、let、run…等等,這些函數是由 Kotlin 的標準函式庫提供,其目的是在對象物件的上下文執行代碼區塊。這些函數稱之為標準函數 (Scope Function)。
使用標準函數有幾個優點:
1. 在這些代碼區塊中,您不需要呼叫對象的名稱就能使用。
2. 提供更清晰的程式碼語意,讓讀程式碼的也成為一件容易的事情。
apply
apply
函數可視為一種配置函數:傳入一個接收者,然後呼叫一系列函數來對接收者做設定。- 傳入的接收者為傳入的對象物件,回傳值是對象本身。
例如:有一個 File 的實例被建立出來之後,立刻呼叫三個 File 內的函數其設定值。
val menuFile = File("menu-file.txt")
menuFile.setReadable(true)
menuFile.setWritable(true)
menuFile.setExecutable(false)
用 apply 改寫
val menuFile = File("menu-file.txt").apply {
setReadable(true)
setWritable(true)
setExecutable(false)
}
- 使用 apply 改寫之後,在語意方面,可以明顯地發現,File 執行了三個動作。
- 另外,因為
File("menu-file.txt")
會直接傳入apply 函數中,所以可以在 apply 函數中直接呼叫File("menu-file.txt")
的函數。
let
let
函數能使某個變數作用於其 lambda 運算式,其回傳值是 lambda 的結果。- 在
let
函數中,可以使用it
表示 傳入的對象。
例如:有一個 list ,我們想要計算第一個項目的平方值。
val firstElement = listOf(1,2,3).first()
val firstItemSquared = firstElement * firstElement
用 let 改寫
val firstItemSquared = listOf(1,2,3).first().let{
it * it
}
- 用
let
改寫之後,不需要額外的暫存變數 firstElement,在let
函數中,可以使用it
關鍵字代表傳入的listOf(1,2,3).first()
run
- 與 apply 一樣都是把接收者傳入 lambda 函數中,不一樣的是 run 回傳的是 lambda 的回傳值,apply 沒有回傳值。
run
適合用在需要對對象物件作設定並且執行運算取得一個結果。- 特別注意的是,當執行
run
函數的值為 null 時,run
的區塊是不會進行操作的。
例如:將
service
對象物件的port
設定為 8080,並且將service.port
利用query
函式帶入取得 result。
service.port = 8080
val result = query(prepareRequest() + " to port ${service.port}")
用 run 改寫
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
- 可以清楚地知道,在
run
函數中,將port
設定為 8080, - 設定完
port
之後在query
函數中,代入port
的值。 - 注意到,
run
裡面就不需要使用service.port
,因為service
已經代入至run
中,所以可以直接呼叫port
。
with
with
函數是run
函數的變形:他們的功能與行為一樣,接收者是使用with
的對象物件,with
的結果則是lambda
的結果。- 但 with 的呼叫方式不同,使用 with 時,要求引數作為其第一個參數傳入,另外,若執行
run
的對象物件為null
,則run
區塊不會進入;反之,with
函數則是會進去操作。
val stringTooLong = with("long long long string"){
length > 10
}
println(stringTooLong)
//true
also
- 將呼叫
also
的物件,以it
代入also
中。回傳的值則是該物件。 also
適合針對同一原始物件,透過副作用做事。當看到also
的時候表示,同時還要針對該對象物件執行此動作。
例如:
有一個MutableList
設定完之後,列印該 List,並且在列印完之後,再增加一個項目。
val numbers = mutableListOf("one", "two", "three")
println("The list elements before adding new one: $numbers")
numbers.add("four")
用 also
改寫
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
- 因為
also
回傳的是傳入對象,所以可以直接串接add("four")
。 - 讀這段程式碼就會清楚的知道:建立完這個
Mutableist
之後做了兩件事。(1). 列印一段文字,裡面包含 List 的內容,(2). 在 List 中新增一個項目。 - 別忘了,在
also
裡面呼叫對象函數,需要使用it
關鍵字。
takeIf
takeIf
函數需要判斷 lambda 中提供的條件運算式 (Predicate),如果是 true ,則返回接收者物件,否則返回 null。
例如:有一段程式碼,呼叫
File
建立一個檔案實例,當這個檔案可以讀寫,才能夠讀取裡面的值,否則會回傳 null。
val file = File("myFile.txt")
val fileContents = if(file.canRead && file.canWrite){
file.readText()
}else{
null
}
用 takeIf 改寫
val fileContents = File("myFile.txt")
.takeIf{ it.canRead() && it.canWrite() }
?.readText()
takeIf
不需要file
的臨時變數,類似於 if ,但是他的優勢在於可以直接在物件的實例上呼叫。- 加上
takeIf
函數如果是false
則會回傳null
,所以可以用空運算子,寫出較簡潔的程式碼。
takeUnless
- 與 takeIf 相反,當 takeUnless 裡面的值為 false 時,才會返回接收者物件,否則返回 null。
結論
Kotlin 的 Lambda 函數讓程式碼簡潔許多,再加上本篇文章所介紹的標準函數,可以減少更多臨時的變數,增加程式的可讀性。很多程式碼都有包含這些標準函數,讀完這篇之後,看到時再也不會搞不清楚狀況了。
附上標準函數的使用表:
如果這篇有幫助到你,幫我拍手鼓勵一下吧。
謝謝