今天要介紹很常在Kotlin 中看見的寫法,lambda,直接開始吧!
lambda可以當作函數類型(function type) 來使用,意思是可以當作宣告變數指定的類型、當引數傳入另一個函數和函數回傳值,用法十分靈活。
豆知識:為什麼叫lambda?lambda可以用希臘字母「λ」來表示,是lambda演算的簡稱。lambda演算是一套數學演算邏輯,...。引自:Kotlin權威2.0
若一個函數的lambda參數排在最後或是唯一的參數,可省略lambda引數的一對小括號,比如String的count函數:
//原本:
"barbiqla".count({it=='b'})
//簡略
"barbiqla".count{it=='b'}
要想將函數當作值存入變數便要使用 ::運算子
fun main() {
val goodMorningGreeting = ::goodMorning
goodMorningGreeting() // 控制台印出"good morning."
}
fun goodMorning(){
println("good morning.")
}
使用val或var關鍵字重構 goodMorning function,如此賦值給goodMorningGreeting時便不需要::運算子,並且呼叫兩者並運行,系統會執行 lambda 運算式的本體,印出"good morning."
fun main() {
val goodMorningGreeting = goodMorning
goodMorningGreeting() // 控制台印出"good morning."
goodMorning() // 控制台印出"good morning."
}
val goodMorning = {
println("good morning.")
}
函數類型不是函數還分了很多種類型,意思是可以像資料類型這樣當作一類類型來使用。
寫一個goodnight變數的資料類型指定為() -> Unit
來比較,不寫的類型也一樣是() -> Unit
。
若是換成參數要整數、回傳值是整數的函數類型就寫為(Int)->Int
val goodMorning = {
println("good morning.")
}
val goodNight:()->Unit = {
println("good night.")
}
既然可以當做資料類型使用,那麼可以當回傳值的類型也不難理解吧。
將早晚問候整合成一個 greeting function:
//整合早安晚安的問候function,返回函數類型`()->Unit`:
fun greeting(isDay:Boolean):()->Unit{
if(isDay){
return goodMornig
}else{
return goodNight
}
}
//早安和晚安的變數:
val goodMorning = {
println("good morning.")
}
val goodNight:()->Unit = {
println("good night.")
}
在函數()中加入參數名並指定型別,
如下列,加入飲品參數,但參數中並沒有指明哪一類飲品,並設定只有早上有飲料喝。而參數類型(Int)->String
也只須給類型就好,不必再為它們取名字。
註:下面程式碼設定傳入Int跟數量1只為了方便接下來的說明。
fun greeting(isDay: Boolean,drink : (Int)->String): () -> Unit {
if (isDay) {
println(drink(1))
return goodMorning
} else {
return goodNight
}
}
接下來定義上面飲品函數並儲存在變數milk與wine中:
可以看見milk的部分將參數Int可以自訂名為"number",而wine的部分無自訂名稱的話,kotlin會以"it"來代指參數Int
fun main() {
val milk :(Int)->String = {number ->
"have $number glass of milk."
}
val wine :(Int)->String = {
"have $it glass of wine."
}
val goodMorningGreeting = greeting(true,milk)
val goodNightGreeting = greeting(false,wine)
goodMorningGreeting()
goodNightGreeting()
}
像本例,晚上並沒有要喝飲料,其實不必硬傳飲料引數給他,但是greeting function 要求必須給該如何是好?
這邊就要講到空值null的概念(下面有簡述)。
在資料類型上加問號(如:Int?、String?)來表示這東西可能為空,而函數類型也可以依樣畫葫蘆,加上問號表示可空,在greeting function的drink參數加上表示可空的問號:
fun greeting(
isDay: Boolean,
drink: ((Int) -> String)?= null //將函數類型括號起來打問號
): () -> Unit {
if (isDay) {
//因應drink的可空性必須做出處理,使它判斷不為空時才執行印出字串,否則編譯不過。
if (drink != null) println(drink(1))
return goodMorning
} else {
return goodNight
}
}
註 :
null : Null維基百科,是一個概念,表示不存在或無意義的值。
(空引用的發明者Tony Hoare自稱是價值百億美元的錯誤發明)
可空性:kotlin在資料類型上加問號(如:Int?、String?)來表示這東西可能、可以為空,並會要求開發人員對可空性的物件做出處理,這是Kotlin為了對應null所可能導致的異常而做出的設計。
NullPointException引起的問題多廣泛,由知名的stackoverflow網站上,什麼是NullPointException,怎麼修復它
這個問題已經被瀏覽的次數可見一斑。
drink參數設好可空性後,晚上就可以不必傳入飲品了:
val goodNightGreeting = greeting(false)
完整的程式碼:
fun main() {
val milk :(Int)->String= {
"have $it glass of milk."
}
// val wine :(Int)->String= {
// "have $it glass of wine."
// }
val goodMorningGreeting = greeting(true,milk)
val goodNightGreeting = greeting(false)
goodMorningGreeting()
goodNightGreeting()
//結果: have 1 glass of milk.
// good morning.
// good night.
}
fun greeting(
isDay: Boolean,
drink: ((Int) -> String)?= null
): () -> Unit {
if (isDay) {
if (drink != null) println(drink(1))
return goodMorning
} else {
return goodNight
}
}
val goodMorning = {
println("good morning.")
}
val goodNight: () -> Unit = {
println("good night.")
}
像milk()只用在一個地方,並不需要重複使用的情況,而且lambda運算是只是一個常值的話(如:1是Int常值、true是布林值常值、"hello"是String常值),可以將lambda運算式直接傳入函數,直接看例子比較快:
//原本
val milk :(Int)->String= { "have $it glass of milk."}
val goodMorningGreeting = greeting(true,milk)
//lambda傳入函數
// 刪除milk()
val goodMorningGreeting = greeting(true,{"have $it glass of milk."})
//照一開說過的簡略語法,當lambda是唯一或最後一個參數可以移出()
val goodMorningGreeting = greeting(true){"have $it glass of milk."}
goodMorningGreeting()
// 結果: have $it glass of milk.
// good morning.
參考codelab:在 Kotlin 中使用函式類型和 lambda 運算式
明天見,掰