【Kotlin】run , let, apply, also, let
https://developer.android.com/codelabs/java-to-kotlin#0
https://kotlinlang.org/docs/scope-functions.html
|
功能 |
物件引用 |
傳回值 |
是擴充函數 |
|---|---|---|---|
|
|
|
Lambda result |
Yes |
|
|
|
Lambda result |
Yes |
|
|
- |
Lambda result |
否:在沒有上下文物件的情況下調用 |
|
|
|
Lambda result |
否:將上下文物件作為參數。 |
|
|
|
Context object |
Yes |
|
|
|
Context object |
Yes |
- 對不可為 null 的物件執行 lambda:let
- 在局部範圍內引入表達式作為變數:let
- 物件配置:apply
- 物件配置和計算結果:run
- 在需要表達式的地方運行語句:非擴展run
- 附加效果:also
- 將物件的函數呼叫進行分組:with
差異
- 他們引用上下文物件的方式。
- 他們的返回值。
fun main() {
val str = "Hello"
// this
str.run {
println("The string's length: $length")
//println("The string's length: ${this.length}") // does the same
}
// it
str.let {
println("The string's length is ${it.length}")
}
}
this
run、with、 並透過關鍵字apply引用上下文物件作為 lambda接收器this。因此,在它們的 lambda 中,物件就像在普通類別函數中一樣可用。
val adam = Person("Adam")
.apply {
age = 20 // same as this.age = 20
city = "London"
}
println(adam)
it
反過來說,let 和 also 會將上下文對象作為 lambda 參數引用。如果沒有指定參數名稱,則會使用默認名稱 it 來訪問對象。it 比 this 短,並且使用 it 的表達式通常更易於閱讀。
然而,當調用對象的函數或屬性時,你無法像使用 this 一樣隱式地獲取對象。因此,當對象主要用作函數調用中的參數時,通過 it 訪問上下文對象更好。如果在代碼塊中使用多個變量,使用 it 也是更好的選擇。
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
println(i)
// INFO: getRandomInt() generated value 78
// 78
value 的 lambda 參數引用:fun getRandomInt(): Int {
return Random.nextInt(100).also { value ->
writeToLog("getRandomInt() generated value $value")
}
}
val i = getRandomInt()
println(i)
// INFO: getRandomInt() generated value 4
// 4
傳回值
上下文對象 (Context object)
apply 和 also val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }
.apply {
add(2.71)
add(3.14)
add(1.0)
}
.also { println("Sorting the list") }
.sort()
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
Lambda result
let、run 和 with val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run {
add("four")
add("five")
count { it.endsWith("e") }
}
println("There are $countEndsWithE elements that end with e.")
// There are 3 elements that end with e.
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
val firstItem = first()
val lastItem = last()
println("First item: $firstItem, last item: $lastItem")
}
// First item: one, last item: three
Functions
let
- 上下文物件可用作參數 (it)。
在代碼中,let 可以理解為“使用這個物件,做以下操作,並返回最後一個操作”
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)
// [5, 4, 4]
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
// and more function calls if needed
}
// [5, 4, 4]
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let(::println)
// [5, 4, 4]
let 通常用於執行包含非空值的代碼塊。要對非空對象執行操作,可以使用安全調用運算符 ?.,並在其 lambda 中調用 let 來執行操作。
val str: String? = "Hello"
//processNonNullString(str) // compilation error: str can be null
val length = str?.let {
println("let() called on $it")
processNonNullString(it) // OK: 'it' is not null inside '?.let { }'
it.length
}
let 引入具有有限作用域的局部變量,以使你的代碼更易於閱讀。要為上下文對象定義一個新變量,可以將其名稱作為 lambda 參數提供,這樣它可以代替默認的 it 使用。val str: String? = "Hello"
//processNonNullString(str) // compilation error: str can be null
val length = str?.let {
println("let() called on $it")
processNonNullString(it) // OK: 'it' is not null inside '?.let { }'
it.length
}
// let() called on Hello
with
- 上下文物件可用作接收器 (this)。
由於 with 不是擴展函數:上下文對象作為參數傳遞,但在 lambda 內部,它可以作為接收者 (this) 使用。
我們建議在不需要返回結果時使用 with 來調用上下文對象上的函數。
在代碼中,with 可以理解為“使用這個對象,做以下操作。”
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
// 'with' is called with argument [one, two, three]
// It contains 3 elements
with val run
- 上下文物件可用作接收器 (this)。
run 的功能與 with 相似,但它是作為擴展函數實現的。因此,像 let 一樣,你可以使用點符號在上下文對象上調用它。
run 當你的 lambda 內部,它可用作接收器同時初始化對象和計算返回值時非常有用。
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// the same code written with let() function:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
// Result for query 'Default request to port 8080'
// Result for query 'Default request to port 8080'
你也可以以非擴展函數的形式調用 run。非擴展變體的 run 沒有上下文對象,但仍然返回 lambda 的結果。非擴展 run 允許你在需要表達式的地方執行多個語句的代碼塊。在代碼中,非擴展 run 可以理解為“運行代碼塊並計算結果。”
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
for (this)。val getHexNumberRegex: () -> Regex = {
val digits = "apply
- 上下文物件可用作接收器 (
this)this)。
apply apply 的最常見用例是對對象進行配置。這類調用可以理解為“將以下賦值應用到物件上。”val also
- 上下文物件可用作參數 (
it)it)。