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) {...}
- off是預設值為0的Int型別引數
- 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 + 3
與1 shl (2 + 3)
0 until n * 2
與0 until (n * 2)
xs union ys as Set<*>
與xs union (ys as Set<*>)
另一方面,中綴函式呼叫的優先順序高於布林操作符
&&
與||
、is-
與in-
檢測以及其他一些操作符。這些表示式也是等價的:
a && b xor c
與a && (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
-
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表示式~
-
如果函式的最後一個引數是一個函式,並且你傳遞一個 lambda 表示式作為相應的引數,你可以在圓括號之外指定它
例如:
val product = items.fold(1, { acc, e -> acc * e }) => val product = items.fold(1) { acc, e -> acc * e }
-
如果一個函式的引數只有一個,並且引數也是一個函式,那麼可以省略圓括號
例如:
run ({ println("...") }) => run { println("...") }
總結
由於Kotlin裡面Lambda表示式的使用還是很多的,所以建議大家多去看看其他的部落格,因為我覺得官方文件對於沒接觸過Lambda的初學者來說還是太難懂了一點。
Day 4 - Learn Kotlin Trip, Completed.