kotlin-let,run,with,apply,also作用域函數詳解

在這裏插入圖片描述

前言

五個作用域,會比較長。
這篇文章,類似於字典。查查。用用。

主要需要了解的點
① 有什麼區別?
② 如何選擇?


作用域函數是什麼意思呢

通過編譯器的手段增加一些操作符,使代碼變得更簡潔
所以,你不用它也完全可以實現相同的功能

它提供了一個臨時作用域,讓對象執行代碼塊的 代碼看起來更簡潔

感受一下 作用域函數 帶來的代碼整潔
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
所以這裏,你會用到 ?.操作符 進行判空

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章