前言
五個作用域,會比較長。
這篇文章,類似於字典。查查。用用。
主要需要了解的點
① 有什麼區別?
② 如何選擇?
作用域函數是什麼意思呢
通過編譯器的手段增加一些操作符,使代碼變得更簡潔
所以,你不用它也完全可以實現相同的功能。
它提供了一個臨時作用域,讓對象執行代碼塊的 代碼看起來更簡潔
感受一下 作用域函數 帶來的代碼整潔
data class Person(var name: String, var age: Int, var city: String) {
fun moveTo(newCity: String) { city = newCity }
fun incrementAge() { age++ }
}
fun main() {
Person("Alice", 20, "Amsterdam").let {
println(it)
it.moveTo("London")
it.incrementAge()
println(it)
}
}
我們將main()函數的代碼改成普通的方式
val alice = Person("Alice", 20, "Amsterdam")
println(alice)
alice.moveTo("London")
alice.incrementAge()
println(alice)
使用 let 之後,代碼更顯層次,更方便的尋找 Person alice的作用域(有效範圍)
作用域函數的弊端是什麼
顯然代碼整潔了
但是可讀性卻下降了。lambda過多就會導致修改起來很累。
要考慮到交接成本。後來者可能改不動你的代碼
Kotlin有五個範圍函數
- let
- run
- with
- apply
- also
5個範圍函數的區別
《kotlin - let,run,with,apply,also作用域函數的區別》
具體講講這5個範圍函數的用法
很多時候這5個範圍函數是可以互換的
同一個邏輯可以用不同的範圍函數來實現
接下來講講 常見的使用方法
let
上下文對象: it
返回結果: lambda結果
普通寫法
fun main() {
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)
}
用法1
重寫部分使用了某變量的邏輯,顯得更有代碼結構層次感
改寫普通寫法
fun main() {
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
}
}
若代碼塊 僅包含一個it參數的單一函數
還可以用反射進行改寫
fun main() {
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let(::println)
}
用法2
搭配 ?. 進行非空判斷
fun processNonNullString(str: String) {}
fun main() {
val str: String? = null
//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
}
}
這裏str爲null,則let不會被執行。若str有值,則let代碼塊可以被執行
用法3
使用 it的名別 來提高代碼可閱讀性
fun main() {
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'")
}
這裏的firstItem即it的別名。從字面上可以更清楚的制度這裏的it是什麼
with
上下文對象: this
返回結果: lambda結果
with可以被解讀爲: 請用這個object做以下的事情
用法1
用numbers去打印log
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
用法2
調用其functions
fun main() {
val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
"The first element is ${this.first()}," +
" the last element is ${last()}"
}
println(firstAndLast)
}
這裏會輸出
The first element is one, the last element is three
run
上下文對象: this
返回結果: lambda結果
它可以獨立運行
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
可以看到沒有 context對象
這裏範例返回的就是Regex
像let一樣用
let用的是it,run用的是this
範例
class Person(var name: String, var height: Int) {
fun signIn(): String = "$name has sign in"
fun getHeight(height: String): String = "Height = '$height'"
}
fun main() {
val alice = Person("Alice", 188)
val result = alice.run {
this.name = "$name the Student"
println(signIn() + " to port " + getHeight("$height"))
"alice clone1 success" //輸出最後一行的結果
}
// the same code written with let() function:
val result2 = alice.let {
it.name = "${it.name} the Student"
println(it.signIn() + " to port " + it.getHeight("${it.height}"))
"alice clone2 success" //輸出最後一行的結果
}
println(result)
println(result2)
}
輸出結果呢
Alice the Student has sign in to port Height = '188'
Alice the Student the Student has sign in to port Height = '188'
alice clone1 success
alice clone2 success
apply
上下文對象: this
返回結果: 上下文對象
使用apply的代碼塊沒有返回值,主要是操作上的接收器對象的成員
主要用來進行對象配置
範例
data class Person(var name: String, var age: Int = 0, var city: String = "")
fun main() {
val adam = Person("Adam").apply {
age = 32
city = "London"
}
}
also
上下文對象: it
返回結果: 上下文對象
這可以理解爲 我也要做什麼什麼
定義爲附加功能最合適
順便打個log吧
fun main() {
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
}
takeIf與takeUnless
takeIf
針對單個對象的過濾功能
匹配則返回該對象,不匹配返回null
takeUnless
與takeIf相反
注意
takeIf和takeUnless是過濾
返回的結果 是 原先的對象
這可以達到鏈式調用的效果!!!
takeIf和takeUnless可以和五個作用域函數 搭配使用
範例
fun main() {
val number = 6
val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
println("even: $evenOrNull, odd: $oddOrNull")
val number2 = 7
val evenOrNull2 = number2.takeIf { it % 2 == 0 }
val oddOrNull2 = number2.takeUnless { it % 2 == 0 }
println("even: $evenOrNull2, odd: $oddOrNull2")
}
可以 看出來takeIf與takeUnless的結果可能是null
所以這裏,你會用到 ?.操作符 進行判空