如何使用 Scope Functions

如何使用 Scope Functions

情境

在寫 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