Kotlin函數(shù)式編程筆記

Kotlin語言基礎筆記

Kotlin流程控制語句筆記

Kotlin操作符重載與中綴表示法筆記

Kotlin擴展函數(shù)和擴展屬性筆記

Kotlin空指針安全(null-safety)筆記

Kotlin類型系統(tǒng)筆記

Kotlin面向?qū)ο缶幊坦P記

Kotlin委托(Delegation)筆記

Kotlin泛型型筆記

Kotlin函數(shù)式編程筆記

Kotlin與Java互操作筆記

Kotlin協(xié)程筆記

就像在Java的世界中,一切皆是對象。那么在函數(shù)式編程中當然一切皆是函數(shù)。函數(shù)式編程感覺像返璞歸真,現(xiàn)在又火了起來。Kotlin作為一位這幾年才出現(xiàn)的語言必然會支持函數(shù)式編程。在Kotlin中函數(shù)式一等公民,地位和對象一樣高,你可以在方法中輸入函數(shù),也可以返回函數(shù)。相對于Scala的學院派風格,Kotlin則是純的的工程派:實用性、簡潔性上都要比Scala要好。怪不得我當年看Scala中感覺好像門檻有點高,上手比較難。

1. 高階函數(shù)

高階函數(shù)是將函數(shù)用作參數(shù)或者返回值的函數(shù)。Kotlin的Collection類型中有大量的這種高階函數(shù),例如:Iterable的filter函數(shù)

filter

其中predicate: (T) -> Boolean定義了一個函數(shù)變量,變量名是predicate,類型是(T) -> Boolean這樣一個函數(shù)。這個函數(shù)表示輸入?yún)?shù)是T類型,輸出Boolean類型。那我們來定義這樣一個函數(shù),然后調(diào)用高階函數(shù)Iterable.filter

fun isLargeThanFive(x: Int): Boolean = x > 5
fun main(args: Array<String>) {
     val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
     println(numbers.filter(::isLargeThanFive))  //打印[6, 7, 8]
}

注意:我們使用::來引用一個函數(shù)。

2. 匿名函數(shù)

匿名函數(shù),其實就是沒有函數(shù)名的函數(shù)。我們也可以使用匿名函數(shù)來實現(xiàn)上面的代碼:

fun main(args: Array<String>) {
     val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
     println(numbers.filter(
               fun (x: Int):Boolean {
                    return x > 5
               }
          )
     )
     //or 寫成一行
     //println(numbers.filter(fun(x: Int) = x > 5))
}

3. Lambda表達式

我們也可以使用更簡單Lambda表達式來實現(xiàn):

    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
    numbers.filter({ it > 5 })
    //or 這樣
    numbers.filter { it > 5 }

Lambda表達式是這樣定義的:

  • lambda 表達式總是括在花括號中。
  • 其參數(shù)(如果有的話)在 -> 之前聲明(參數(shù)類型可以省略)。
  • 函數(shù)體(如果存在的話)在 -> 后面。
  • 如果lambda表達式是該調(diào)用的唯一函數(shù),則調(diào)用中的圓括號可以省略。
  • 如果函數(shù)的最后一個參數(shù)是一個函數(shù),并且你傳遞一個 lambda 表達式作為相應的參數(shù),你可以在圓括號之外指定它。

對于最后一點,我們可以看看Iterable.elementAtOrElse方法:

elementAtOrElse

這個方法是返回第index元素,如果不存在的話返回(Int) -> T的函數(shù)值,函數(shù)參數(shù)是index,返回T類型。我們可以看看如何調(diào)用:

    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
    println(numbers.elementAtOrElse(10, { 100}))
    //或者可以這樣。
    println(numbers.elementAtOrElse(10) {100})

我們現(xiàn)在來定義一個兩個參數(shù)的lambda表達式:

    val max = { x: Int, y: Int -> if (x > y) x else y }
    //下面這句等同于上面這句代碼。
    //val max: (Int, Int) -> Int = { x: Int, y: Int -> if (x > y) x else y }
    println(max(1, 2))

更復雜的,lambda表達式還可以返回一個lambda表達式:

    val sum = { x: Int ->  {y:Int -> x+y } }
    println(sum)  //打印(kotlin.Int) -> (kotlin.Int) -> kotlin.Int

    println(sum(2)(1))  //打印3

3.1 it單個參數(shù)的隱私名稱

當lambda表達式只有一個參數(shù)時,那么它的聲明可以省略(包括->)。可以用it代表lambda唯一的參數(shù)。

    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
    numbers.filter({ it > 5 })
    //or 這樣
    numbers.filter { it > 5 }

3.2 下劃線用于未使用的變量(1.1版本起)

如果 lambda 表達式的參數(shù)未使用,那么可以用下劃線取代其名稱:

map.forEach { _, value -> println("$value!") }

4. 閉包

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

fun main(args: Array<String>) {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
    var sum = 0
    numbers.filter { it > 5 }.forEach { sum += it }
    println(sum)  //打印21
}

5. 帶接收者的函數(shù)字面值

使用匿名函數(shù)的語法,我們可以直接指定函數(shù)字面值的接收者類型。

val sum = fun Int.(other: Int): Int = this + other
println(1.sum(1))  打印2

當接收者類型可以從上下文推斷時,lambda 表達式可以用作帶接收者的函數(shù)字面值。

class HTML {
    fun body() {
        println("HTML BODY")
    }
}

fun html(init: HTML.() -> Unit): HTML { // HTML.()中的HTML是接受者類型
    val html = HTML()  // 創(chuàng)建接收者對象
    html.init()        // 將該接收者對象傳給該 lambda
    return html
}

fun main(args: Array<String>) {
    html{
        body()
    }  //打印HTML BODY
}

這個特性可以讓我們構(gòu)建DSL語言,類似安卓中build.gradle中的語法。

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