# 【Kotlin】Scope functions ( run , let, apply, also, let, takeIf and takeUnless)

- 出處
- [https://developer.android.com/codelabs/java-to-kotlin#0](https://developer.android.com/codelabs/java-to-kotlin#0)
- [https://kotlinlang.org/docs/scope-functions.html](https://kotlinlang.org/docs/scope-functions.html)

```Java
// 原寫法
        val mary = Person("mary")
        mary.age = 20
        mary.birthplace = "NewYork"
        println(mary) // name:mary, age:20, birthplace:NewYork
  
// 使用scope function 簡化語法加強語意
        val john = Person("John") .apply {
            age = 18
            birthplace = "Taipei"
        }
		println(john) // name:John, age:18, birthplace:Taipei
```

```JavaScript
// Scope functions 可以做到一樣的事，只是寫法不同
// 印出 list 字元長度大於3 的資料
// let, also, run, apply, with
fun main() {    
    
    val numbers = mutableListOf("one", "two", "three", "four", "five")
  	// [3, 3, 5, 4, 4]
    
    // it代表自己
	numbers.map { it.length }.filter { it > 3 }.let { 
    	println(it)
    }
    // 命名(item)代表自己
    numbers.map { it.length }.filter { it > 3 }.let { item ->
    	println(item)
    }
    
    // it代表自己
    numbers.map { it.length }.filter { it > 3 }.also {
    	println(it)
    }
    // 命名(item)代表自己
    numbers.map { it.length }.filter { it > 3 }.also { item ->
    	println(item)
    }
    
    // this代表自己
    numbers.map { it.length }.filter { it > 3 }.run { 
    	println(this)
    }
    
    // this代表自己
    numbers.map { it.length }.filter { it > 3 }.apply { 
    	println(this)
    }
    
    // this代表自己
	val gt3Numbers = numbers.map { it.length }.filter { it > 3 }
    with(gt3Numbers){ 
    	println(this)
	}
}

// 結果都是 [5, 4, 4]
```

#### \[官方\]判斷如何使用何種function

[![image-1721202686908.png](https://bookstack.treemanou.com/uploads/images/gallery/2024-07/scaled-1680-/CErx1SpwR1EXyfiB-image-1721202686908.png)](https://bookstack.treemanou.com/uploads/images/gallery/2024-07/CErx1SpwR1EXyfiB-image-1721202686908.png)

<table class="table__content table__content--wide" id="bkmrk-%E5%8A%9F%E8%83%BD-%E7%89%A9%E4%BB%B6%E5%BC%95%E7%94%A8-%E5%82%B3%E5%9B%9E%E5%80%BC-%E6%98%AF%E6%93%B4%E5%85%85%E5%87%BD%E6%95%B8-le" style="width: 155.684%; height: 269px;"><thead class="table__thead"><tr class="table__tr" style="height: 35.3906px;"><th class="table__th" style="width: 11.9775%; height: 35.3906px;">功能

</th><th class="table__th" style="width: 13.1539%; height: 35.3906px;">物件引用

</th><th class="table__th" style="width: 19.5811%; height: 35.3906px;">傳回值

</th><th class="table__th" style="width: 30.4295%; height: 35.3906px;">是擴充函數

</th><th style="width: 24.627%;"></th></tr></thead><tbody class="table__tbody"><tr class="table__tr" id="bkmrk-let-it-lambda-result" style="height: 35.3906px;"><td class="table__td" id="bkmrk-let-0" style="width: 11.9775%; height: 35.3906px;">`let`

</td><td class="table__td" id="bkmrk-it-0" style="width: 13.1539%; height: 35.3906px;">`it`

</td><td class="table__td" id="bkmrk-lambda-result-0" style="width: 19.5811%; height: 35.3906px;">Lambda result

</td><td class="table__td" id="bkmrk-yes" style="width: 30.4295%; height: 35.3906px;">Yes

</td><td style="width: 24.627%;">使用這個物件，做以下操作，並返回最後一個操作<span style="background-color: #ffff00;">  
非null物件處理</span>

</td></tr><tr class="table__tr" id="bkmrk-run-this-lambda-resu" style="height: 35.3906px;"><td class="table__td" id="bkmrk-run-0" style="width: 11.9775%; height: 35.3906px;">`run`

</td><td class="table__td" id="bkmrk-this-0" style="width: 13.1539%; height: 35.3906px;">`this`

</td><td class="table__td" id="bkmrk-lambda-result-1" style="width: 19.5811%; height: 35.3906px;">Lambda result

</td><td class="table__td" id="bkmrk-yes-0" style="width: 30.4295%; height: 35.3906px;">Yes

</td><td style="width: 24.627%;">lambda 同時<span style="background-color: #ffff00;">初始化對象</span>和計算返回值

</td></tr><tr class="table__tr" id="bkmrk-run---lambda-result-" style="height: 35.3906px;"><td class="table__td" id="bkmrk-run-1" style="width: 11.9775%; height: 35.3906px;">`run`

</td><td class="table__td" id="bkmrk--" style="width: 13.1539%; height: 35.3906px;">-

</td><td class="table__td" id="bkmrk-lambda-result-2" style="width: 19.5811%; height: 35.3906px;">Lambda result

</td><td class="table__td" style="width: 30.4295%; height: 35.3906px;">否：在沒有上下文物件的情況下調用

</td><td style="width: 24.627%;"><span style="background-color: #ffff00;">運行代碼塊並計算結果(lambda)</span>

</td></tr><tr class="table__tr" id="bkmrk-with-this-lambda-res" style="height: 35.3906px;"><td class="table__td" id="bkmrk-with-0" style="width: 11.9775%; height: 35.3906px;">`with`

</td><td class="table__td" id="bkmrk-this-1" style="width: 13.1539%; height: 35.3906px;">`this`

</td><td class="table__td" id="bkmrk-lambda-result-3" style="width: 19.5811%; height: 35.3906px;">Lambda result

</td><td class="table__td" style="width: 30.4295%; height: 35.3906px;">否：將上下文物件作為參數。

</td><td style="width: 24.627%;"><span style="background-color: #ffff00;">使用這個物件，做以下操作</span>

</td></tr><tr class="table__tr" id="bkmrk-apply-this-context-o" style="height: 35.3906px;"><td class="table__td" id="bkmrk-apply-0" style="width: 11.9775%; height: 35.3906px;">`apply`

</td><td class="table__td" id="bkmrk-this-2" style="width: 13.1539%; height: 35.3906px;">`this`

</td><td class="table__td" id="bkmrk-context-object" style="width: 19.5811%; height: 35.3906px;">Context object

</td><td class="table__td" id="bkmrk-yes-1" style="width: 30.4295%; height: 35.3906px;">Yes

</td><td style="width: 24.627%;"><span style="background-color: #ffff00;">將以下賦值應用到物件上</span>

</td></tr><tr class="table__tr" id="bkmrk-also-it-context-obje" style="height: 35.3906px;"><td class="table__td" id="bkmrk-also-0" style="width: 11.9775%; height: 35.3906px;">`also`

</td><td class="table__td" id="bkmrk-it-1" style="width: 13.1539%; height: 35.3906px;">`it`

</td><td class="table__td" id="bkmrk-context-object-0" style="width: 19.5811%; height: 35.3906px;">Context object

</td><td class="table__td" id="bkmrk-yes-2" style="width: 30.4295%; height: 35.3906px;">Yes

</td><td style="width: 24.627%;"><span style="background-color: #ffff00;">並且還可以對該物件執行以下操作</span>

</td></tr></tbody></table>

<div id="bkmrk-%E5%9B%9E%E5%82%B3%E5%80%BC-%5C-%E5%82%B3%E5%85%A5%E5%8F%83%E8%80%83%E7%89%A9%E4%BB%B6-this-la"><table border="1" style="border-collapse: collapse; width: 100%; height: 87px;"><tbody><tr style="height: 29px;"><td style="width: 33.3333%; height: 29px;">回傳值 \\ 傳入參考物件</td><td style="width: 33.3333%; height: 29px;">this</td><td style="width: 33.3333%; height: 29px;">lambda(it)</td></tr><tr style="height: 29px;"><td style="width: 33.3333%; height: 29px;">this</td><td style="width: 33.3333%; height: 29px;"><span style="background-color: #ffff99;">apply</span>

</td><td style="width: 33.3333%; height: 29px;"><span style="background-color: #ffff99;">also</span></td></tr><tr style="height: 29px;"><td style="width: 33.3333%; height: 29px;">bock return (回傳最後一行結果)</td><td style="width: 33.3333%; height: 29px;"><span style="background-color: #ffff99;">run</span></td><td style="width: 33.3333%; height: 29px;"><span style="background-color: #ffff99;">let</span></td></tr></tbody></table>

</div><div id="bkmrk-%E4%B8%8B%E9%9D%A2%E7%9A%84%E5%B0%88%E9%96%80%E9%83%A8%E5%88%86%E6%8F%90%E4%BE%9B%E4%BA%86%E6%9C%89%E9%97%9C%E9%80%99%E4%BA%9B%E5%8A%9F%E8%83%BD%E7%9A%84%E8%A9%B3%E7%B4%B0%E8%B3%87">下面的專門部分提供了有關這些功能的詳細資訊。</div><div id="bkmrk-%E4%BB%A5%E4%B8%8B%E6%98%AF%E6%A0%B9%E6%93%9A%E9%A0%90%E6%9C%9F%E7%9B%AE%E7%9A%84%E9%81%B8%E6%93%87%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%87%BD%E6%95%B8%E7%9A%84%E7%B0%A1%E7%9F%AD%E6%8C%87">以下是根據預期目的選擇作用域函數的簡短指南：  
  
</div>- 對不可為 null 的物件執行 lambda：let
- 在局部範圍內引入表達式作為變數：let
- 物件配置：apply
- 物件配置和計算結果(初始化物件)：run
- 在需要表達式的地方運行語句(lambda)：非擴展run
- 附加效果：also
- 將物件的函數進行分組呼叫(Grouping function calls on an objec)：with

---

### 差異

<div id="bkmrk-%E7%94%B1%E6%96%BC%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%87%BD%E6%95%B8%E6%9C%AC%E8%B3%AA%E4%B8%8A%E5%BE%88%E7%9B%B8%E4%BC%BC%EF%BC%8C%E5%9B%A0%E6%AD%A4%E4%BA%86%E8%A7%A3%E5%AE%83%E5%80%91">由於作用域函數本質上很相似，因此了解它們之間的差異非常重要。每個作用域函數之間有兩個主要差異：</div>- 他們引用上下文物件(Context object)的方式。
- 他們的返回值。

在傳遞給作用域函數的 lambda 中，上下文物件可透過短引用而不是其實際名稱來取得。  
每個作用域函數使用兩種方式之一來引用上下文物件：  
作為 lambda接收器( `this`) 或作為 lambda 參數 ( `it`)。  
兩者都提供相同的功能，因此我們針對不同的用例描述了每種方法的優缺點，並提供了使用建議。

```JavaScript
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 中，物件就像在普通類別函數中一樣可用。

<div id="bkmrk-%E5%A4%A7%E5%A4%9A%E6%95%B8%E6%83%85%E6%B3%81%E4%B8%8B%EF%BC%8C%E6%82%A8%E5%8F%AF%E4%BB%A5this%E5%9C%A8%E5%AD%98%E5%8F%96%E6%8E%A5%E6%94%B6%E8%80%85">大多數情況下，您可以this在存取接收者物件的成員時省略，從而使程式碼更短。另一方面，如果this省略，則很難區分接收者成員和外部物件或函數。</div><div id="bkmrk-%E5%9B%A0%E6%AD%A4this%EF%BC%8C%E5%B0%8D%E6%96%BC%E4%B8%BB%E8%A6%81%E9%80%8F%E9%81%8E%E5%91%BC%E5%8F%AB%E5%85%B6%E5%87%BD%E6%95%B8%E6%88%96%E7%82%BA">因此this，對於主要透過<span style="background-color: #ffff00;">呼叫其函數或為屬性</span><span style="background-color: #ffcc00;">賦值</span>來操作物件成員的 lambda，建議將上下文物件作為接收者 ()。</div>```JavaScript
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` 也是更好的選擇。

```JavaScript
fun getRandomInt(): Int {
    return Random.nextInt(100).also {
        writeToLog("getRandomInt() generated value $it")
    }
}

val i = getRandomInt()
println(i)

// INFO: getRandomInt() generated value 78
// 78
```

<div id="bkmrk--1"></div><div id="bkmrk-%E4%BB%A5%E4%B8%8B%E7%AF%84%E4%BE%8B%E5%B1%95%E7%A4%BA%E4%BA%86%E5%A6%82%E4%BD%95%E5%B0%87%E4%B8%8A%E4%B8%8B%E6%96%87%E5%B0%8D%E8%B1%A1%E4%BD%9C%E7%82%BA%E5%B8%B6%E6%9C%89%E5%8F%83">以下範例展示了如何將上下文對象作為帶有參數名稱 `value` 的 lambda 參數引用：</div>```JavaScript
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
```

### 傳回值

<div id="bkmrk-%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%87%BD%E6%95%B8%E5%9B%A0%E5%85%B6%E5%82%B3%E5%9B%9E%E7%9A%84%E7%B5%90%E6%9E%9C%E8%80%8C%E6%9C%89%E6%89%80%E4%B8%8D%E5%90%8C%EF%BC%9A">作用域函數因其傳回的結果而有所不同：</div><div id="bkmrk--2"></div><div id="bkmrk-%E6%82%A8%E6%87%89%E8%A9%B2%E6%A0%B9%E6%93%9A%E6%82%A8%E6%8E%A5%E4%B8%8B%E4%BE%86%E6%83%B3%E8%A6%81%E5%9C%A8%E7%A8%8B%E5%BC%8F%E7%A2%BC%E4%B8%AD%E5%9F%B7%E8%A1%8C%E7%9A%84%E6%93%8D">您應該根據您接下來想要在程式碼中執行的操作仔細考慮您想要的回傳值。這可以幫助您選擇要使用的最佳範圍函數。</div><div id="bkmrk--3"></div>#### 上下文對象 (Context object)

<div id="bkmrk-apply-%E5%92%8C-also-%E7%9A%84%E8%BF%94%E5%9B%9E%E5%80%BC%E6%98%AF%E4%B8%8A%E4%B8%8B">`apply` 和 `also` 的<span style="background-color: #ffff00;">返回值</span>是上下文<span style="background-color: #ffff00;">對象本身</span>。因此，它們可以作為副步驟包含在調用鏈中：你可以在同一對象上<span style="background-color: #ffff00;">連續調用函數</span>。</div>```JavaScript
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()
```

<div id="bkmrk--4"></div><div id="bkmrk-%E5%AE%83%E5%80%91%E4%B9%9F%E5%8F%AF%E4%BB%A5%E7%94%A8%E6%96%BC%E8%BF%94%E5%9B%9E%E4%B8%8A%E4%B8%8B%E6%96%87%E5%B0%8D%E8%B1%A1%E7%9A%84%E5%87%BD%E6%95%B8%E7%9A%84%E8%BF%94%E5%9B%9E">它們也可以用於返回上下文對象的函數的返回語句中。</div>```JavaScript
fun getRandomInt(): Int {
    return Random.nextInt(100).also {
        writeToLog("getRandomInt() generated value $it")
    }
}

val i = getRandomInt()
```

#### Lambda result

<div id="bkmrk-let%E3%80%81run-%E5%92%8C-with-%E8%BF%94%E5%9B%9E%E7%9A%84%E6%98%AF-">`let`、`run` 和 `with` <span style="background-color: #ffff00;">返回的</span>是 lambda 表達式的<span style="background-color: #ffff00;">結果</span>。因此，你可以在將結果賦值給變量、在結果上鏈式操作等情況下使用它們。</div>```JavaScript
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.
```

<div id="bkmrk-%E6%AD%A4%E5%A4%96%EF%BC%8C%E4%BD%A0%E5%8F%AF%E4%BB%A5%E5%BF%BD%E7%95%A5%E8%BF%94%E5%9B%9E%E5%80%BC%EF%BC%8C%E4%BD%BF%E7%94%A8%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%87%BD%E6%95%B8%E7%82%BA">此外，你可以忽略返回值，使用作用域函數為局部變量創建一個臨時作用域。</div>```JavaScript
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

<div id="bkmrk-%E7%82%BA%E4%BA%86%E5%B9%AB%E5%8A%A9%E4%BD%A0%E9%81%B8%E6%93%87%E9%81%A9%E5%90%88%E4%BD%A0%E7%9A%84%E4%BD%BF%E7%94%A8%E5%A0%B4%E6%99%AF%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%87%BD">為了幫助你選擇適合你的使用場景的作用域函數，我們將詳細描述它們並提供使用建議。</div><div id="bkmrk--5"></div>#### **let**

- 上下文物件可用作參數 (it)。
- 傳回值是 lambda 結果。

在代碼中，`let` 可以理解為“<span style="background-color: #ffff00;">使用這個物件，做以下操作，並返回最後一個操作</span>”

<div id="bkmrk-let%E5%8F%AF%E7%94%A8%E6%96%BC%E5%9C%A8%E5%91%BC%E5%8F%AB%E9%8F%88%E7%9A%84%E7%B5%90%E6%9E%9C%E4%B8%8A%E5%91%BC%E5%8F%AB%E4%B8%80%E5%80%8B%E6%88%96%E5%A4%9A">let可用於在呼叫鏈的結果上呼叫一個或多個函數。例如，以下程式碼列印集合上兩個操作的結果：</div>```JavaScript
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)

// [5, 4, 4]
```

<div id="bkmrk--6"></div><div id="bkmrk-%E4%BD%BF%E7%94%A8let%EF%BC%8C%E6%82%A8%E5%8F%AF%E4%BB%A5%E9%87%8D%E5%AF%AB%E4%B8%8A%E9%9D%A2%E7%9A%84%E7%AF%84%E4%BE%8B%EF%BC%8C%E9%80%99%E6%A8%A3%E6%82%A8">使用let，您可以重寫上面的範例，這樣您就不會將清單操作的結果指派給變數：</div>```JavaScript
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]
```

<div id="bkmrk--7"></div><div id="bkmrk-%E5%A6%82%E6%9E%9C%E5%82%B3%E9%81%9E%E7%B5%A6%E7%9A%84%E7%A8%8B%E5%BC%8F%E7%A2%BC%E5%8D%80%E5%A1%8Alet%E5%8C%85%E5%90%AB%E5%96%AE%E4%B8%80%E5%87%BD%E6%95%B8">如果傳遞給的程式碼區塊let包含單一函數作為it參數，則可以使用方法來引用 (::) 而不是 lambda 參數：</div>```JavaScript
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let(::println)

// [5, 4, 4]
```

<div id="bkmrk--8"></div>`let` 通常用於執行包含非空值的代碼塊。要對非空對象執行操作，可以使用安全調用運算符 `?.`，並在其 lambda 中調用 `let` 來執行操作。

```JavaScript
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
}
```

<div id="bkmrk-%E4%BD%A0%E4%B9%9F%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8-let-%E5%BC%95%E5%85%A5%E5%85%B7%E6%9C%89%E6%9C%89%E9%99%90%E4%BD%9C%E7%94%A8%E5%9F%9F">你也可以使用 `let` 引入具有有限作用域的局部變量，以使你的代碼更易於閱讀。要為上下文對象定義一個新變量，可以將其名稱作為 lambda 參數提供，這樣它可以代替默認的 `it` 使用。</div>```JavaScript
val numbers = listOf("one", "two", "three", "four")
val modifiedFirstItem = numbers.first().let { firstItem ->
    println("The first item of the list is '$firstItem'")
    if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"
}.uppercase()
println("First item after modifications: '$modifiedFirstItem'")

// The first item of the list is 'one'
// First item after modifications: '!ONE!'
```

#### **with**

- 上下文物件可用作接收器 (this)。
- 傳回值是 lambda 結果。

由於 `with` 不是擴展函數：上下文對象作為參數傳遞，但在 lambda 內部，它可以作為接收者 (`this`) 使用。

我們建議在<span style="background-color: #ffff00;">不需要返回結果</span>時使用 `with` 來調用上下文對象上的函數。  
在代碼中，`with` 可以理解為“<span style="background-color: #ffff00;">使用這個對象，做以下操作。</span>”

```JavaScript
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
```

<div id="bkmrk--9"></div><div id="bkmrk-%E4%BD%A0%E4%B9%9F%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8-with-%E5%BC%95%E5%85%A5%E4%B8%80%E5%80%8B%E8%BC%94%E5%8A%A9%E5%B0%8D%E8%B1%A1">你也可以使用 `with` 引入一個輔助對象，其屬性或函數用於計算值。</div>```JavaScript
val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
    "The first element is ${first()}," +
    " the last element is ${last()}"
}
println(firstAndLast)
```

#### **run**

- 上下文物件可用作接收器 (this)。
- 傳回值是 lambda 結果。

`run` 的功能與 `with` 相似，但它是作為擴展函數實現的。因此，像 `let` 一樣，你可以使用點符號在上下文對象上調用它。

`run` 當你的 lambda 同時<span style="background-color: #ffff00;">初始化對象</span>和<span style="background-color: #ffff00;">計算返回值</span>時非常有用。

```JavaScript
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` 可以理解為“<span style="background-color: #ffff00;">運行代碼塊並計算結果</span>。”

```JavaScript
val hexNumberRegex = run {
    val digits = "0-9"
    val hexDigits = "A-Fa-f"
    val sign = "+-"

    Regex("[$sign]?[$digits$hexDigits]+")
}

for (match in hexNumberRegex.findAll("+123 -FFFF !%*& 88 XYZ")) {
    println(match.value)
}

// +123
// -FFFF
// 88
```

```JavaScript
val getHexNumberRegex: () -> Regex = {
    val digits = "0-9"
    val hexDigits = "A-Fa-f"
    val sign = "+-"

    return Regex("[$sign]?[$digits$hexDigits]+")
}

```

#### **apply**

- 上下文物件可用作接收器 (<span style="background-color: #ffff00;">this</span>)。
- 傳回值是<span style="background-color: #ffff00;">物件本身</span>。

<div id="bkmrk-apply-%E8%BF%94%E5%9B%9E%E4%B8%8A%E4%B8%8B%E6%96%87%E5%B0%8D%E8%B1%A1%E6%9C%AC%E8%BA%AB%EF%BC%8C%E5%9B%A0%E6%AD%A4%E5%BB%BA%E8%AD%B0">`apply` 返回上下文對象本身，因此建議在<span style="background-color: #ffff00;">不返回值</span>的代碼塊中使用它，並且主要操作接收者對象的成員。`apply` 的最常見用例是對對象進行配置。這類調用可以理解為“<span style="background-color: #ffff00;">將以下賦值應用到物件上</span>。”</div>```JavaScript
val adam = Person("Adam").apply {
    age = 32
    city = "London"        
}
println(adam)

 // Person(name=Adam, age=32, city=London)
```

另一個 `apply` 的使用場景是將其包含在多個調用鏈中以進行更複雜的處理。

```JavaScript
data class Address(var street: String = "", var city: String = "")
data class Person(var name: String = "", var address: Address = Address())

val person = Person().apply {
    name = "Alice"
    address = Address().apply {
        street = "123 Main St"
        city = "Wonderland"
    }
}

println(person) // Output: Person(name=Alice, address=Address(street=123 Main St, city=Wonderland))

```

在這個例子中，我們使用 `apply` 配置 `Person` 對象的屬性，並在內部使用另一個 `apply` 來配置 `Address` 對象。這樣的鏈式調用使得代碼結構更清晰，易於維護。

#### **also**

- 上下文物件可用作參數 (<span style="background-color: #ffff00;">it</span>)。
- 傳回值是<span style="background-color: #ffff00;">物件本身</span>。

<div id="bkmrk-also-%E5%B0%8D%E6%96%BC%E5%9F%B7%E8%A1%8C%E4%B8%80%E4%BA%9B%E9%9C%80%E8%A6%81%E4%B8%8A%E4%B8%8B%E6%96%87%E5%B0%8D%E8%B1%A1%E4%BD%9C%E7%82%BA">`also` 對於執行一些需要上下文對象作為參數的操作非常有用。使用 `also` 用於那些<span style="background-color: #ffff00;">需要對象引用</span>而<span style="background-color: #ffff00;">不是其屬性和函數</span>的情況，或者當你不想影響外部作用域的 `this` 參考時。</div><div id="bkmrk--10"></div><div id="bkmrk-%E7%95%B6%E6%82%A8also%E5%9C%A8%E7%A8%8B%E5%BC%8F%E7%A2%BC%E4%B8%AD%E7%9C%8B%E5%88%B0%E6%99%82%EF%BC%8C%E6%82%A8%E5%8F%AF%E4%BB%A5%E5%B0%87%E5%85%B6">當您also在程式碼中看到時，您可以將其讀作"<span style="background-color: #ffff00;">並且還可以對該物件執行以下操作</span>。" (or 插入log ??)</div>```JavaScript
val numbers = mutableListOf("one", "two", "three")
numbers
    .also { println("The list elements before adding new one: $it") }
    .add("four")


// The list elements before adding new one: [one, two, three]
```

#### <span class="title__content">takeIf and takeUnless</span>

`takeIf` 和 `takeUnless` 是 Kotlin 標準庫中的函數，允許你在調用鏈中嵌入對對象狀態的檢查。

當對一個對象調用 `takeIf` 並傳遞一個條件時，如果該對象滿足該條件，則返回這個對象；否則返回 `null`。因此，`takeIf` 是針對單個對象的過濾函數。

```JavaScript
val number = Random.nextInt(100)

val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
println("even: $evenOrNull, odd: $oddOrNull")

// even: 62, odd: null
```

在使用 `takeIf` 和 `takeUnless` 之後鏈式調用其他函數時，請不要忘記執行空值檢查或使用安全調用運算符 `?.`，因為它們的返回值是可為空的。

```JavaScript
val str = "Hello"
val caps = str.takeIf { it.isNotEmpty() }?.uppercase()
//val caps = str.takeIf { it.isNotEmpty() }.uppercase() //compilation error
println(caps)

// HELLO
```

<div id="bkmrk--11"><div><div><div><div><svg class="icon-md" fill="none" height="41" role="img" viewbox="0 0 41 41" width="41" xmlns="http://www.w3.org/2000/svg"></svg></div></div></div></div></div>`takeIf` 和 `takeUnless` 與作用域函數結合使用時特別有用。例如，你可以將 `takeIf` 和 `takeUnless` 與 `let` 鏈式調用，以便在符合給定條件的對象上執行代碼塊。具體來說，先對對象調用 `takeIf`，然後使用安全調用（`?.`）調用 `let`。對於不符合條件的對象，`takeIf` 會返回 `null`，因此 `let` 不會被調用。

```JavaScript
fun displaySubstringPosition(input: String, sub: String) {
    input.indexOf(sub).takeIf { it >= 0 }?.let {
        println("The substring $sub is found in $input.")
        println("Its start position is $it.")
    }
}

displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")

// The substring 11 is found in 010000011.
// Its start position is 7.
```

以下是一個示例，展示如何在不使用 `takeIf` 或作用域函數的情況下編寫相同的功能：

```JavaScript
fun displaySubstringPosition(input: String, sub: String) {
    val index = input.indexOf(sub)
    if (index >= 0) {
        println("The substring $sub is found in $input.")
        println("Its start position is $index.")
    }
}

displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")

// The substring 11 is found in 010000011.
// Its start position is 7.
```

---