kotlin - let,run,with,apply,also作用域函數的區別

在這裏插入圖片描述

兩個主要區別

  • 上下文對象用this 還是 it
  • 返回值

上下文對象用this 還是 it

this和it是作用域函數獲取對象引用的 短名稱, 兩者都提供相同的功能

使用this的情況

範圍函數: run,with,apply 使用this

在大多數情況下,this訪問接收器對象的成員時可以省略,從而使代碼更短

比如這個例子就是省略了this
data class Person(var name: String, var age: Int = 0, var city: String = "")

fun main() {
    val adam = Person("Adam").apply { 
        age = 20                       // same as this.age = 20 or adam.age = 20
        city = "London"
    }
}

但是不加this,就不容易區分age、city是來自內部接收器Person 還是 外部成員或功能

大家看下不容易區分的情況
data class Person(var name: String, var age: Int = 0, var city: String = "")

var otherName = "wangxueming"

fun main() {
    val adam = Person("Adam").apply { 
        age = 20                       // same as this.age = 20 or adam.age = 20
        city = "London"
        otherName = "wxm"
    }
    println("age = ${adam.age}; city = ${adam.city}; otherName = ${otherName}")
}

這裏的otherName你就不容易看出來到底是Person的變量還是外部變量。

使用it的情況

範圍函數: let,also使用it

  • it相較this更爲簡潔
  • 但在範圍域內this就不可用了
看範例
import kotlin.random.Random

fun writeToLog(message: String) {
    println("INFO: $message")
}

fun main() {
    fun getRandomInt(): Int {
        return Random.nextInt(100).also {
            writeToLog("getRandomInt() generated value $it")
            //下面這行的this會報編譯錯誤:'this' is not defined in this context
            //writeToLog("getRandomInt() generated value $this")
        }
    }

    val i = getRandomInt()
}

it在lambda中是支持自定義名稱的

將上個範例中的getRandomInt(),從it改成value

fun getRandomInt(): Int {
        return Random.nextInt(100).also { value -> {
            writeToLog("getRandomInt() generated value $value")
            writeToLog("getRandomInt() generated 222 value $value")
        }
        }
    }

返回值

apply、also返回上下文對象
let、run、with返回lambda結果

返回 上下文對象

apply、also會返回 上下文對象;
但apply、also發起者是 上下文對象;
這就是個鏈式調用嘛,準確的是是對fun的鏈式調用

鏈式調用就可以這麼寫了
fun main() {
    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()
    println(numberList)
}

進一步理解一下,in是上下文對象,out依舊是上下文對象
這說明 also和apply不對原先的操作造成任何影響
原來的是fun,添加了also或apply之後,返回的依舊是fun
also、apply可以理解爲對原先 fun的補充邏輯處理

我們再看一遍RandomInt的例子
import kotlin.random.Random

fun writeToLog(message: String) {
    println("INFO: $message")
}

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

    val i = getRandomInt()
}

添加also之後,Random.nextInt(100)不受影響,只是額外打印了一行log
這就可以動態的 給fun添加一個原先fun處理完成後的邏輯

返回lambda表達式結果

let、run、with會返回 lambda表達式結果
這就提供了 將fun計算的結果傳給下一個函數繼續計算
這是對結果的鏈接調用

看個統計末尾 字母爲e個數的範例
fun main() {
    val numbers = mutableListOf("one", "two", "three")
    val countEndsWithE = numbers.run { 
        add("four")
        val Res1 = count { it.startsWith("f") }
        println("Res1 = ${Res1}")
        add("five")
        val Res2 = count { it.startsWith("f") }
        println("Res2 = ${Res2}")
        println("${numbers}")
        count { it.endsWith("e")&&it.startsWith("t") }
    }
    println("There are $countEndsWithE elements that end with e.")
}

輸出結果爲

Res1 = 1
Res2 = 2
[one, two, three, four, five]
There are 1 elements that end with e.

正如文章開頭說的,作用於表達式 只是書寫的簡化
相當於是 多行 邏輯處理的整合
每一行代碼都能依次執行,並及時的得到相應的結果


函數選擇

功能 對象參考 返回值 是擴展功能 適用情況
let it Lambda結果 僅支持在非null對象上使用;將 表達式 像變量一樣引入到 作用域中
run this Lambda結果 對象配置和計算結果
run Lambda結果 否:在沒有上下文對象的情況下調用 需要表達式的運行語句
with this Lambda結果 否:將上下文對象作爲參數。 對對象進行分組功能調用
apply this 上下文對象 對象配置
also it 上下文對象 附加功能
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章