Kotlin學習筆記(14)- lambda

系列文章全部為本人的學習筆記,若有任何不妥之處,隨時歡迎拍磚指正。如果你覺得我的文章對你有用,歡迎關注我,我們一起學習進步!
Kotlin學習筆記(1)- 環境配置
Kotlin學習筆記(2)- 空安全
Kotlin學習筆記(3)- 語法
Kotlin學習筆記(4)- 流程控制
Kotlin學習筆記(5)- 類
Kotlin學習筆記(6)- 屬性
Kotlin學習筆記(7)- 接口
Kotlin學習筆記(8)- 擴展
Kotlin學習筆記(8)- 擴展(續)
Kotlin學習筆記(9)- 數據類
Kotlin學習筆記(10)- 泛型
Kotlin學習筆記(11)- 內部類和嵌套類
Kotlin學習筆記(12)- 委托
Kotlin學習筆記(13)- 函數式編程
Kotlin學習筆記(14)- lambda

在上一篇《Kotlin學習筆記(13)- 函數式編程》的最后我提到lambda為函數式編程提供了更多更好的實現,如果你還不太了解什么是函數式編程,那么可以看看我的上一篇文章。那么我們首先來看看什么是lambda,在百度百科上是這么說的:

“Lambda 表達式”(lambda expression)是一個匿名函數,Lambda表達式基于數學中的λ演算得名,直接對應于其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。Lambda表達式可以表示閉包(注意和數學傳統意義上的不同)。

一、Lambda的定義

看上面的說法有點抽象,有點不明所以,我們先來看一個栗子

class Num {
    fun logic(a: Int, b: Int, calc: (Int, Int) -> Int){
        println("calc : ${calc(a,b)}")
    }
}

fun main(args : Array<String>){
    val num = Num()
    num.logic(1, 2, {x,y -> x+y})
}

// 輸出
calc : 3

這個栗子和上一篇文章很像,只是在調用的時候改成了Lambda方式:num.logic(1, 2, {x,y -> x+y}),其中{x,y -> x+y}就是我們今天要講的Lambda表達式,它的完整格式應該是這樣

{ x: Int, y: Int -> x + y }

寫成java代碼是這個樣子:

public int sum(int x, int y){
    return x+y;
}

可以很明顯看出有幾個規則:

  1. 參數寫在->左邊,格式與普通函數的參數格式一樣,多個參數用逗號,分割
  2. 參數的類型可選,可忽略,編輯器會根據上下文推斷(這就和普通函數不一樣了吧,有種“我知道你懂得,所以就不寫了”的感覺有木有,知己的感覺啊……)
  3. 函數體跟在->右邊
  4. Lambda表達式總是被大括號{}包圍著

二、Lambda中的一些約定

  1. 如果 Kotlin 可以自己計算出簽名,它允許我們不聲明唯一的參數,并且將隱含地為我們聲明其名稱為 it。其實通常情況下它都可以自己計算出簽名,也就是說,如果函數字面值只有一個參數, 那么它的聲明可以省略(連同 ->),其名稱是 it

    fun oneParams(one : (Int) -> Int){
        println("oneParams : ${one(5)}")
    }
    
    fun main(args : Array<String>){
        val num = Num()
        num.oneParams({it * 2})
    }
    
    // 輸出
    oneParams : 10
    

    說到省略,其實還有一種情況可以省略->,大家應該也能想到,就是無參函數。

    fun empty(emptyM : () -> Unit){
        emptyM()
    }
    
    fun main(args : Array<String>){
        val num = Num()
        num.empty({println("empty method")})
    }
    
    // 輸出
    empty method
    
  2. 如果Lambda中的某個參數沒有用到,可以用下劃線_代替,也就是說,省了好多請名字的腦細胞有木有!這個特性從1.1開始可以使用,現在你看到的時候應該已經不止1.1了吧,所以這個限制看看就好~

    fun unusedParams(unused : (Int,Int) -> Int){
        println("unusedParams : ${unused(5,10)}")
    }
    
    fun main(args : Array<String>){
        val num = Num()
        num.unusedParams { _, used -> used * 2 }
    }
    
    // 輸出
    unusedParams : 20
    
  3. 如果函數的最后一個參數是一個函數,那么我們在用Lambda表達最后一個函數參數的時候,可以把它放在括號()外面,所以下面的寫法是等價的。

    class Num {
        fun logic(a: Int, b: Int, calc: (Int, Int) -> Int){
            println("calc : ${calc(a,b)}")
        }
        fun sum(a: Int, b: Int) = a + b
    }
    
    fun main(args : Array<String>){
        val num = Num()
        // 寫法1
        num.logic(1, 2, {x : Int,y : Int -> x+y})
        // 寫法2
        num.logic(1, 2){x : Int,y : Int -> x+y}
        // 寫法3
        num.logic(1, 2){x,y -> x+y}
    }
    

    那么這么寫有什么好處呢?難道只是位置變了一下?當然不是,不要忘記,Lambda的->后面是方法體,也就是很多時候不是想栗子中這樣只有一行,如果有多行的話,體會一下他們的區別:

    num.logic(1, 2, {x,y ->
        println("extra line")
        x+y
    })
    
    num.logic(1, 2){x,y ->
        println("extra line")
        x+y
    }
    

    是不是感覺下面的寫法要優雅很多,也明確很多?

  4. 其實在上面一點應該已經能看出,如果有需要的話,Lambda會隱式的返回最后一個表達式的值,就像上面的最后一行x+y。當然,我們也可以顯示的表達返回值,下面的寫法還是一樣的:

    // 寫法1
    num.unusedParams { _, used ->
        println("print first")
        return@unusedParams used * 2
    }
    
    // 寫法2
    num.unusedParams { _, used ->
        println("print first")
        used * 2
    }
    

三、匿名函數

看了這么多,我們發現一個問題,Lambda的返回類型全是自動推斷的,雖然很人性化,但是有時候我們就是想自己指定類型怎么辦?當然是有辦法的,那就是匿名函數

fun(x:Int, y:Int):Int{return x+y}

匿名函數看起來非常像一個常規函數聲明,除了其名稱省略了。其函數體可以是表達式(如上所示)或代碼塊。由于這已經不算正經的Lambda,所以它不需要被大括號{}包裹,也不能像Lambda一樣寫在括號()外,而調用也是直接調用。

num.logic(1, 2, fun(x:Int, y:Int):Int{return x+y})

四、閉包

Lambda 表達式或者匿名函數(以及局部函數對象表達式) 可以訪問其 閉包 ,即在外部作用域中聲明的變量。 與 Java 不同的是可以修改閉包中捕獲的變量:

var sum = 0
ints.filter { it > 0 }.forEach {
    sum += it
}
print(sum)

五、小結

其實沒什么小結,關于更多的函數式的高級應用、復雜應用,個人也正在學習,大家互相交流。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容