情境
在寫 Kotlin 的時候,常常會看到很多關鍵字如:let、apply、also、with and run,而透過這些關鍵字包起來的範圍,我們稱之為 Scoping Functions ,這些關鍵字好像互換也沒問題,就會很好奇,它們到底差別在哪裡?
那為什麼要分成這麼多種 function 呢?原因很簡單,它們都有各自的使用情境,因此,我們來介紹每一個的使用範疇跟規則。
假設我們有一個類別如下所示:
data class Person(var name: String, var age: Int)
接下來會以這個類別貫穿全文。
Extension Function
- 在使用 Scope Functions 的時候,是否要把物件帶入,如果要把物件帶入,那麼就可以透過
?.
來判斷該物件是否為空?如果為空,則不繼續執行下去。
person?.let{}
這邊有兩個 Scope Functions 不是
Extension Function : run、with。
run{
print("hello")
}
如果以 extension function 的形式出現,則 Object reference 會是以 this
的方式出現。
person.run{
age = 18
}
Object reference
- 傳進 Scope 的 context object 是 it 或 this。
it 跟 this 差別在哪裡呢?
以下用 this 當作範例來說明,我們對 Person 類別設定一些屬性值。
val givemepass = Person("givemepass1", 30)
println(givemepass)
givemepass.name = "givemepass2"
givemepass.age = 20
println(givemepass)
在上面這段程式碼,就會看到一個現象,不斷地透過某個物件進行操作,如果有一個方法可以幫我解決這樣的問題,那該有多好?
所以我們找了 Scope Functions 的 apply 來幫我們達成這件事情,只要透過 apply 傳入的區塊,則會傳入 this 來代表我們外部 Person 物件。
Person("givemepass", 30).apply {
println(this)
name = "givemepass1"
age = 20
println(this)
}
這兩段程式碼其實是等價的,但是下面的程式碼相對可讀性就比較高,因為沒有不斷呼叫自己的雜訊來干擾。
那 it 情境該是如何?
val numList = arrayOf(1, 2, 3, 4, 5).filter { it >= 3 }
println(numList)
上面的範例可以會多存一個變數,並且透過 print 方法把陣列印出來,但是其實有更好的寫法,我們可以透過 let 這個 Scope Function 幫忙處理,他會將外部的 array 物件以 it 的方式呈現在區塊內。
arrayOf(1, 2, 3, 4, 5).filter { it >= 3 }.let{ print(it) }
使用 it 當作 Object reference 的有兩個,分別是:let、also。
Return Value
- 當區塊結束時,會以傳入的物件或者最後一行當作回傳值,回傳的值會有兩種,分別是 context object 以及 lambda result。
傳入的假設是 context object,代表著透過這個物件,我們可以進行 scope 的程式執行,來達到我們想要做的事情。
以下例子以 run 為主,將 name 這個變數回傳回去讓 nameStr 接收,因為 run 的特性就是最後一行是將結果傳到區塊範圍外面。
val nameStr = person.run{
name = "givemepass"
name
}
Lambda
- Lambda 表示式 : 在一個方法內傳入一段待執行的程式碼。
假設有個方法定義如下:
fun abc(num: Int, action: (Int) -> Unit) {
action(num)
}
在這樣的一個情況,abc 這個方法就能夠透過這個參數,來呼叫開發者定義的程式片段,跟 Java 的匿名函式一樣,可以這樣操作。
abc(1, {
print("input number is $it")
})
上述這個 {} 區塊就是一個 Lambda。
講了這麼多,其實官方有整理一個清楚的表格。
Function | Object reference | Return value | Is extension function |
---|---|---|---|
let |
it |
Lambda result | Yes |
run |
this |
Lambda result | Yes |
run |
- | Lambda result | No: called without the context object |
with |
this |
Lambda result | No: takes the context object as an argument. |
apply |
this |
Context object | Yes |
also |
it |
Context object | Yes |
整理一下相關的適用情境,這不是絕對,它們之間其實可以互相通用,只是從字面上的意思可能的情境如下。
- let for null checks
- run for configuration
- with for change instance properties
- apply for register
- also for log information