1. 程式人生 > >Kotlin學習之旅(D4)-函式與Lambda表示式

Kotlin學習之旅(D4)-函式與Lambda表示式

Kotlin學習之旅-第四天

今天的主題是:函式與Lambda表示式

前言

函式

Kotlin裡面的函式其實在之前的學習中已經見過了,通過 fun 關鍵字來標識

fun double(x: Int): Int {
    return 2 * x
}

預設引數

除了一般的使用 x: Int 這種方式定義引數,我們也可以使用預設引數的方式,這種方式可以有效減少過載方法的數量

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {...}
  1. off是預設值為0的Int型別引數
  2. len是預設值為b.size的Int型別引數

命名引數

在Java裡面,我們呼叫具有多個引數的方法,是無法直接看到每個引數的意思的,例如:

private void reformat(String str, Boolean normalizeCase, Boolean upperCaseFirstLetter, Boolean, divideByCamelHumps, Char wordSeparator){
    ...
}

// 呼叫方法
reformat(str, true, ture, ture, '_') 

在Kotlin裡面,我們可以在呼叫的時候給引數加個名字,就像:

fun reformat(str: String,
             normalizeCase: Boolean = true,
             upperCaseFirstLetter: Boolean = true,
             divideByCamelHumps: Boolean = false,
             wordSeparator: Char = ' ') {
……
}

// 呼叫方法
reformat(str,
    normalizeCase = true,
    upperCaseFirstLetter = true,
    divideByCamelHumps = false,
    wordSeparator = '_'
)

這樣程式碼的可讀性更高,在多個引數的時候能清晰看到每個引數的作用

返回Unit的函式

前面寫的幾個方法都有返回值,那麼如果要像Java裡面一樣返回void,在kotlin要怎麼寫呢?

fun printHello(name: String?): Unit {
    if (name != null)
        println("Hello ${name}")
    else
        println("Hi there!")
    // `return Unit` 或者 `return` 是可選的
}

通過 Unit 關鍵字表示返回 Unit型別,他的作用類似於Java裡面的void,在Kotlin裡面,Unit返回型別是可以省略的。

中綴表示法

標有 infix 關鍵字的函式也可以使用中綴表示法(忽略該呼叫的點與圓括號)呼叫。中綴函式必須滿足以下要求:

infix fun Int.shl(x: Int): Int { …… }

// 用中綴表示法呼叫該函式
1 shl 2

// 等同於這樣
1.shl(2)

中綴函式呼叫的優先順序低於算術操作符、型別轉換以及 rangeTo 操作符。 以下表達式是等價的:

  • 1 shl 2 + 31 shl (2 + 3)
  • 0 until n * 20 until (n * 2)
  • xs union ys as Set<*>xs union (ys as Set<*>)

另一方面,中綴函式呼叫的優先順序高於布林操作符 &&||is-in- 檢測以及其他一些操作符。這些表示式也是等價的:

  • a && b xor ca && (b xor c)
  • a xor b in c(a xor b) in c

完整的優先順序層次結構請參見其語法參考

Lambda 表示式

關於Lambda表示式這部分,從Java8就開始支援了,在實際開發中也會經常用到,但對我來說還是太抽象了,一直都不太理解,因此這一塊我是參照官方文件來寫的。如果有好的學習資料,歡迎大家留言~

lambda 表示式與匿名函式是“函式字面值”,即未宣告的函式, 但立即做為表示式傳遞。考慮下面的例子:

max(strings, { a, b -> a.length < b.length })

函式 max 是一個高階函式,它接受一個函式作為第二個引數。 其第二個引數是一個表示式,它本身是一個函式,即函式字面值,它等價於以下命名函式:

fun compare(a: String, b: String): Boolean = a.length < b.length

從上面的例子看來,其實Lambda就是一種函式的表達方式,這個函式還未宣告,但是通過Lambda表示式可以直接呼叫,只要掌握了語法,就能很方便的寫出來了。

Lambda 表示式語法

Lambda 表示式的完整語法形式如下:

val sum = { x: Int, y: Int -> x + y }

lambda 表示式總是括在花括號中, 完整語法形式的引數宣告放在花括號內,並有可選的型別標註, 函式體跟在一個 -> 符號之後。如果推斷出的該 lambda 的返回型別不是 Unit,那麼該 lambda 主體中的最後一個(或可能是單個)表示式會視為返回值。

如果我們把所有可選標註都留下,看起來如下:

val sum: (Int, Int) -> Int = { x, y -> x + y }

把上面的官方文件說明翻譯一下,就是:

1.lambda表示式總是括在花括號中
2.引數在->之前
3.函式在->之後

因此上面的例子翻譯一下,就是:

1.{ x, y -> x + y } 為lambda表示式
2.(Int, Int) 為引數
3.Int = {...} 就是一個返回型別為Int的函式

it:單個引數的隱式名稱

一個 lambda 表示式只有一個引數是很常見的。

如果編譯器自己可以識別出簽名,也可以不用宣告唯一的引數並忽略 ->。 該引數會隱式宣告為 it

ints.filter { it > 0 } // 這個字面值是“(it: Int) -> Boolean”型別的

幾個Tips

  1. Lambda表示式可以傳遞給一個高階函式當做引數

    fun <T, R> Collection<T>.fold(
        initial: R, 
        combine: (acc: R, nextElement: T) -> R
    ): R {
        var accumulator: R = initial
        for (element: T in this) {
            accumulator = combine(accumulator, element)
        }
        return accumulator
    }
    

    fold這個函式的第二個引數是 combine(R, T),同樣是一個函式,他的返回值會作為fold的引數,那麼flod就是一個高階函式,而combine裡面可以使用Lambda表示式~

  2. 如果函式的最後一個引數是一個函式,並且你傳遞一個 lambda 表示式作為相應的引數,你可以在圓括號之外指定它

    例如:

    val product = items.fold(1, { acc, e -> acc * e }) 
    => 
    val product = items.fold(1) { acc, e -> acc * e }
    
  3. 如果一個函式的引數只有一個,並且引數也是一個函式,那麼可以省略圓括號

    例如:

    run ({ println("...") })
    =>
    run { println("...") }
    

總結

由於Kotlin裡面Lambda表示式的使用還是很多的,所以建議大家多去看看其他的部落格,因為我覺得官方文件對於沒接觸過Lambda的初學者來說還是太難懂了一點。

Day 4 - Learn Kotlin Trip, Completed.