您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關如何辨別Kotlin的作用域函數,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
作用域函數
Kotlin 的作用域函數有五種:let、run、with、apply 以及 also。
這些函數基本上做了同樣的事情:在一個對象上執行一個代碼塊。
下面是作用域函數的典型用法:
val adam = Person("Adam").apply { age = 20 city = "London" } println(adam)
如果不使用 apply 來實現,每次給新創建的對象屬性賦值時就必須重復其名稱。
val adam = Person("Adam") adam.age = 20 adam.city = "London" println(adam)
作用域函數沒有引入任何新的技術,但是它們可以使你的代碼更加簡潔易讀。
事實上,同樣的功能可能多個作用域函數都能實現,但我們應該根據不同的場景和需求來使用合適的作用域函數,以求更優雅的實現。
如果想直接查看作用域函數之間的區別與使用場景歸納表,請點擊這里。
下面我們將詳細描述這些作用域函數的區別及約定用法。
區別
作用域函數之間有兩個主要區別:
引用上下文對象的方式:this 還是 it
在作用域函數的 lambda 表達式里,上下文對象可以不使用其實際名稱而是使用一個更簡短的引用(this 或 it)來訪問。
作用域函數引用上下文對象有兩種方式:
fun main() { val str = "Hello" // this str.run { println("The receiver string length: $length") //println("The receiver string length: ${this.length}") // 和上句效果相同 } // it str.let { println("The receiver string's length is ${it.length}") } }
作為 lambda 表達式的接收者
run、with 以及 apply 將上下文對象作為 lambda 表達式接收者,通過關鍵字 this 引用上下文對象。可以省略 this,使代碼更簡短。
使用場景:主要對上下文對象的成員進行操作(訪問屬性或調用函數)。
val adam = Person("Adam").apply { age = 20 // 和 this.age = 20 或者 adam.age = 20 一樣 city = "London" } println(adam)
作為 lambda 表達式的參數
let 及 also 將上下文對象作為 lambda 表達式參數。如果沒有指定參數名,對象可以用隱式默認名稱 it 訪問。it 比 this 簡短,帶有 it 的表達式通常更容易閱讀。然而,當調用對象函數或屬性時,不能像 this 這樣隱式地訪問對象。
使用場景:主要對上下文對象進行操作,作為參數使用。
fun getRandomInt(): Int { return Random.nextInt(100).also { writeToLog("getRandomInt() generated value $it") } } val i = getRandomInt()
此外,當將上下文對象作為參數傳遞時,可以為上下文對象指定在作用域內的自定義名稱(為了提高代碼的可讀性)。
fun getRandomInt(): Int { return Random.nextInt(100).also { value -> writeToLog("getRandomInt() generated value $value") } } val i = getRandomInt()
返回值
根據返回結果,作用域函數可以分為以下兩類:
可以根據在代碼中的后續操作來選擇適當的函數。
返回上下文對象
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()
它們還可以用在返回上下文對象的函數的 return 語句中。
fun getRandomInt(): Int { return Random.nextInt(100).also { writeToLog("getRandomInt() generated value $it") } } val i = getRandomInt()
返回lambda表達式結果
let、run 及 with 返回 lambda 表達式的結果。所以,在需要使用其結果給一個變量賦值,或者在需要對其結果進行鏈式操作等情況下,可以使用它們。
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.")
此外,還可以忽略返回值,僅使用作用域函數為變量創建一個臨時作用域。
val numbers = mutableListOf("one", "two", "three") with(numbers) { val firstItem = first() val lastItem = last() println("First item: $firstItem, last item: $lastItem") }
約定用法
let
上下文對象 作為 lambda 表達式的 參數(it)來訪問。 返回值 是 lambda 表達式的結果。
let 可用于在調用鏈的結果上調用一個或多個函數。例如,以下代碼打印對集合的兩個操作的結果:
val numbers = mutableListOf("one", "two", "three", "four", "five") val resultList = numbers.map { it.length }.filter { it > 3 } println(resultList)
使用 let,可以寫成這樣:
val numbers = mutableListOf("one", "two", "three", "four", "five") numbers.map { it.length }.filter { it > 3 }.let { println(it) // 如果需要可以調用更多函數 }
若代碼塊僅包含以 it 作為參數的單個函數,則可以使用方法引用(::)代替 lambda 表達式:
val numbers = mutableListOf("one", "two", "three", "four", "five") numbers.map { it.length }.filter { it > 3 }.let(::println)
let 經常用于 僅使用非空值執行代碼塊。如需對非空對象執行操作,可對其使用安全調用操作符 ?. 并調用 let 在 lambda 表達式中執行操作。
val str: String? = "Hello" //processNonNullString(str) // 編譯錯誤:str 可能為空 val length = str?.let { println("let() called on $it") processNonNullString(it) // 編譯通過:'it' 在 '?.let { }' 中必不為空 it.length }
使用 let 的另一種情況是引入作用域受限的局部變量以提高代碼的可讀性。如需為上下文對象定義一個新變量,可提供其名稱作為 lambda 表達式參數來替默認的 it。
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 + "!" }.toUpperCase() println("First item after modifications: '$modifiedFirstItem'")
with
一個非擴展函數:上下文對象作為參數傳遞,但是在 lambda 表達式內部,它可以作為接收者(this)使用。 返回值是lambda 表達式結果。
建議使用 with 來調用上下文對象上的函數,而不使用 lambda 表達式結果。 在代碼中,with 可以理解為“對于這個對象,執行以下操作。”
val numbers = mutableListOf("one", "two", "three") with(numbers) { println("'with' is called with argument $this") println("It contains $size elements") }
with 的另一個使用場景是引入一個輔助對象,其屬性或函數將用于計算一個值。
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 一樣——作為上下文對象的擴展函數.
當 lambda 表達式同時包含對象初始化和返回值的計算時,run 很有用。
val service = MultiportService("https://example.kotlinlang.org", 80) val result = service.run { port = 8080 query(prepareRequest() + " to port $port") } // 同樣的代碼如果用 let() 函數來寫: val letResult = service.let { it.port = 8080 it.query(it.prepareRequest() + " to port ${it.port}") }
除了在接收者對象上調用 run 之外,還可以將其用作非擴展函數。 非擴展 run 可以使你在需要表達式的地方執行一個由多個語句組成的塊。
val hexNumberRegex = run { val digits = "0-9" val hexDigits = "A-Fa-f" val sign = "+-" Regex("[$sign]?[$digits$hexDigits]+") } for (match in hexNumberRegex.findAll("+1234 -FFFF not-a-number")) { println(match.value) }
apply
上下文對象 作為 接收者(this)來訪問。返回值 是上下文對象本身。
對于不返回值且主要在接收者(this)對象的成員上運行的代碼塊使用 apply。apply 的常見情況是對象配置。這樣的調用可以理解為“將以下賦值操作應用于對象”。
val adam = Person("Adam").apply { age = 32 city = "London" } println(adam)
將接收者作為返回值,可以輕松地將 apply 包含到調用鏈中以進行更復雜的處理。
also
上下文對象作為 lambda 表達式的參數(it)來訪問。 返回值 是上下文對象本身。
also 對于執行一些將上下文對象作為參數的操作很有用。 對于需要引用對象而不是其屬性與函數的操作,或者不想屏蔽來自外部作用域的 this 引用時,請使用 also。
當在代碼中看到 also 時,可以將其理解為“并且用該對象執行以下操作”。
val numbers = mutableListOf("one", "two", "three") numbers .also { println("The list elements before adding new one: $it") } .add("four")
總結
下表總結了Kotlin作用域函數的主要區別與使用場景:
函數 | 對象引用 | 返回值 | 是否是擴展函數 | 使用場景 |
---|---|---|---|---|
let | it | Lambda 表達式結果 | 是 | 1. 對一個非空對象執行 lambda 表達式 2. 將表達式作為變量引入為局部作用域中 |
run | this | Lambda 表達式結果 | 是 | 對象配置并且計算結果 |
run | - | Lambda 表達式結果 | 不是:調用無需上下文對象 | 在需要表達式的地方運行語句 |
with | this | Lambda 表達式結果 | 不是:把上下文對象當做參數 | 一個對象的一組函數調用 |
apply | this | 上下文對象 | 是 | 對象配置 |
also | it | 上下文對象 | 是 | 附加效果 |
關于如何辨別Kotlin的作用域函數就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。