Kotlin:入門lambda和高階函式
Kotlin:入門lambda和高階函式
讓我們來看看一些常見的讓新手血壓高的語法糖縮寫是怎麼一步步被創造出來的
lambda表示式入門
首先我們要了解如何定義一個函式型別,括號內寫要傳入的引數型別,多個引數用逗號隔開,->
右邊是返回值型別,無返回值注意寫Unit
,他有點類似於void,不過Unit的話更具備泛型的一些特性,這個有機會再說。同時在表示式內部也很少會用到return返回
(String, Int) -> Unit
-
我們先來宣告一個函式型別,實現傳入一個引數返回一句話的簡單功能:
val testFunction: (String) -> String testFunction = { name -> val saySomething = "Hello" "$saySomething $name" //返回{}內最後一行 } println(testFunction("Tommy"))
這裡我們接收了一個String型別的變數並命名為name;返回一個String型別的變數,就是
{ }
包起來的內容裡的最後一條。然後用等號和大括號為testFunction定義相匹配的函式程式碼。這裡的輸出結果是Hello Tommy
,很好懂。 -
其實等號可以簡寫,比如像這樣修改到到第一行就行:
val testFunction: (String) -> String = { name -> val saySomething = "Hello" "$saySomething $name" }
-
當只有一個引數的時候,我們還能使用
it
val testFunction: (String) -> String = { // 刪去了name -> val saySomething = "Hello" "$saySomething $it" // 原來的name改成了it,結果不變 }
-
那麼非常自然地想到,傳入兩個或多個引數(注意現在可不能用
it
了)也應該可以的吧,比如我還想加入一個Int型變數,只需要稍微修改程式碼即可:val testFunction: (String, Int) -> String = { name, num -> val saySomething = "Hello" "$saySomething $name, you're No.$num" }
這裡就會輸出
Hello Any, you're No.3
到目前為止,都還十分好懂,接下來我們來看看,由於Kotlin優秀的型別推導機制,為我們帶來了哪些縮寫方法
-
首先我們來看看當沒有引數的情況,當然我們直接把口號內的引數刪除就好,這也是非常明顯的做法:
val testFunction: () -> String = { val saySomething = "Hello" "$saySomething" }
但Kotlin的型別推斷機制能夠自動把
{ }
裡的內容所返回的資料型別所推斷出來,因此返回值的String也是可以省略的,就可以改寫成以下形式:val testFunction = { val saySomething = "Hello" "$saySomething" //看這行就知道返回值是String }
好的它現在變成了一個非常精簡的模樣,如果是剛學java就跑來看這個故意還以為你把一個程式碼塊賦值給了一個奇怪的東西,已經失去了常規意義上一個函式該有的模樣。
-
那麼有引數的情況呢?也能這樣子簡寫嗎?當然可以,不過還是有一點區別的,傳入引數名和引數型別還是要宣告的,比如現在我想要把上面兩個引數的情況改寫一下,就變成了這樣:
val testFunction = { name: String, num: Int -> // 在{}括號內聲明瞭引數名和引數型別 val saySomething = "Hello" "$saySomething $name, you're No.$num" }
lambda表示式算是入門了,那麼下面來看看高階函式
高階函式
高階函式的定義也十分簡單,就是一個函式接收另一個函式作為引數,或者返回值的型別是另一個函式,那麼就可以成為高階函式。一般接收或返回的函式就會用到上述所說的lambda表示式。同樣我們採取由冗雜到精簡循序漸進的學法。
-
我們首先來定義一個簡單的高階函式
fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int { val result = operation(num1, num2) return result }
這裡我們定義了一個高階函式num1AndNum2,傳入三個引數,num1,num2,operation,其中operation還是一個傳入兩個整型引數,返回值也是整形的的函式型別引數,對於高階函式num1AndNum2來說,它的返回值為
{ }
裡的返回內容,也是整形。 那麼怎麼使用這個高階函式呢?對於前面的兩個整形引數當然就像以往的方式填入就寫了,問題是operation該傳入什麼引數?答案其實也很明顯,就是傳入一個函式,我們可以像這樣做:
fun plus(num1: Int, num2: Int): Int { return num1 + num2 } fun minus(num1: Int, num2: Int): Int { return num1 - num2 } fun main{ val num1 = 100 val num2 = 80 val result1 = num1AndNum2(num1, num2, ::plus) val result2 = num1AndNum2(num1, num2, ::minus) println("result1 is $result1") println("result2 is $result2") }
這裡我們定義了兩個引數宣告和返回值宣告都與operation引數完全匹配的函式,然後用
::plus
和::minus
的引用寫法即可,最後的執行結果為result1 is 180
和result1 is 20
-
接下來是用lambda表示式來簡寫,程式碼如下
fun main{ val result3 = num1AndNum2(num1, num2) { n1, n2 -> n1 + n2 } val result4 = num1AndNum2(num1, num2) { n1, n2 -> n1 - n2 } println("result3 is $result3") println("result4 is $result4") }
相信有了剛才的lambda表示式入門,也可以很好地理解這一寫法吧?
應用例項
我們已經有了一些基礎了,現在我們來結合擴充套件函式的知識來模擬apply函式的功能吧!
-
我們先來定義一個高階函式()
fun StringBuilder.build(block: StringBuilder.() -> Unit):StringBuilder { block() return this }
這裡我們為StringBuilder類定義了一個拓展函式build,build是一個高階函式,接收一個無參無返回值的引數型別,然後build函式的返回值型別也是StringBuilder。
這時候我們會注意到,在函式型別前面多了一個
StringBuilder.
的語法結構,其實這才是定義高階函式的完整語法規則,在函式型別前加上ClassName.
就表示這個函式型別是定義在哪個類當中的。這樣當我們呼叫build函式時傳入的Lambda表示式將會自動擁有StringBuilder的上下文,也就類似於apply函式的實現方式。下面我們用一個吃水果的例子說明val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape") val result = StringBuilder().build { append("Start eating fruits.\n") for (fruit in list) { append(fruit).append("\n") } append("Ate all fruits.") } println(result.toString())
這樣在用法上和apply函式還是很相像的,不過我們的函式目前只能作用在StringBuilder類上,若想要和apply函式一樣所用在所有類上,還得藉助於Kotlin的泛式才行。