高階函數(shù)
- 定義
函數(shù)的 「參數(shù)」或者 「返回值」 是 「函數(shù)類型」 的函數(shù),成為高階函數(shù)
- 作用
在函數(shù)內(nèi)部可以動態(tài)調(diào)用傳過來的函數(shù),不用像java一樣通過定義接口來回調(diào)
- 使用
/**
* 函數(shù)a 的參數(shù)是一個函數(shù)類型,這個函數(shù)類型的參數(shù)是int,返回值是string
*/
fun a(funParam: (Int) -> String):String{
return funParam(3)
}
fun b(param:Int) :String{
return param.toString()
}
函數(shù)a 的參數(shù)是一個函數(shù)類型,這個這個函數(shù)類型的參數(shù)是int,返回值是string, 所以a 這個函數(shù)為高階函數(shù),高階函數(shù)是對這一類函數(shù)的稱呼,沒有任何其他功能。
高階函數(shù)的調(diào)用方式為
a(::b)
val d = ::b
除了作為函數(shù)的參數(shù)和返回值類型, 還可以將其賦值給變量,對于一個已經(jīng)聲明好的函數(shù),不管是把它作為參數(shù)傳遞給函數(shù),還是賦值給變量,都需要在函數(shù)名的左邊加上雙冒號 「::」, 這個 「::」的寫法叫做函數(shù)引用
- 為什么要加上 「::」
因為加上「::」 后,這個函數(shù)才變成了一個對象,在Kotlin,函數(shù)可以作為參數(shù)傳遞的本質(zhì)是 「函數(shù)可以作為對象存在」, 因為只有對象才可以作為參數(shù)傳遞,賦值也是這樣,這有對象才能賦值給變量 。 但 Kotlin 的函數(shù)的本身的性質(zhì)又決定了它沒辦法被當(dāng)做對象,于是 Kotlin 就創(chuàng)建一個和函數(shù)具有相同功能的對象, 創(chuàng)建的方式就是「::」, 在函數(shù)名的左邊加上「::」,就不表示函數(shù)本身了,而表示的是一個對象,或者說是一個指向?qū)ο蟮囊?。但這個對象可不是函數(shù)本身,而是一個和這個函數(shù)具有相同功能的對象。 怎么用函數(shù),就可以怎么用這個加了「::」的對象。
例如:
a(::b)
val d = ::b
b(1)
d(2) // 地哦啊哦用函數(shù)
(::b)(3)
fun a(funParam: (Int) -> String):String{
return funParam(3)
}
fun b(param:Int) :String{
return param.toString()
}
這個加了「::」的對象,成為函數(shù)對象
- 簡化寫法
要傳一個函數(shù)類型的參數(shù),或者把一個函數(shù)類型的對象賦值給變量,除了用 「::」 來拿現(xiàn)成的函數(shù)使用,還可以直接把這個函數(shù)挪過來,寫成
a(fun b (param:Int):String{
return param.toString()
})
val f = fun b(param:Int):String{
return param.toString()
}
這種函數(shù)的寫法,函數(shù)的名字已經(jīng)不重要了,可以省略變成
a(fun (param:Int):String{
return param.toString()
})
val f = fun(param:Int):String{
return param.toString()
}
這種寫法的函數(shù)叫 「匿名函數(shù)」,因為這個函數(shù)(「=」號右邊)它沒有名字 。需要注意的是「=」號左邊的不是函數(shù)名字,它是變量的名字,這個變量的類型是一種函數(shù)類型。 具體到示例代碼來說,這個函數(shù)類型只有一個參數(shù),參數(shù)類型是Int,返回值是String
Lambda
傳統(tǒng)寫法
textView.setOnClickListener(fun (v: View):Unit{
})
可以寫成 lambda 表達式的形式
textView.setOnClickListener({ v: View ->
})
如果lambda 是函數(shù)的最后一個參數(shù),可以把lambda寫在括號的外面,變成
textView.setOnClickListener(){ v: View ->
}
如果lamada 是 函數(shù)唯一的參數(shù),可以直接把括號去了,變成
textView.setOnClickListener{ v: View ->
}
如果 lamada 函數(shù)是單參數(shù)的,這個單參數(shù)不用的話可以省略不寫,變成
textView.setOnClickListener{
}
其實就算需要用這個單參數(shù)也可以不寫,因為Kotlin對于唯一的參數(shù)有默認的名字:it
所以當(dāng)要把一個匿名函數(shù)賦值給變量,而不是作為函數(shù)參數(shù)傳遞的時候,如果也寫成lamada 的形式,就不能省掉 Lambda 的參數(shù)類型了 , 例如下面的代碼會報錯
var e = {
return it.toString()
}
因為無法從上下文推斷出參數(shù)類型, 如果祥省掉參數(shù)類型,需要給左邊的變量指明類型
var g: (Int) -> String = {
return it.toString()
}
還是會報錯,因為lambda 的返回值不是用return 來返回的,而是取最后一行代碼的值。如果你寫了 return, 會直接結(jié)束外層函數(shù), 如果只是想返回 lambda, 寫 return 就會有問題了。
另外,因為lambda是個代碼塊,它總能根據(jù)最后一行的代碼推斷出返回值類型,所以它的返回值類型確實不寫。而且lambda 在語法上也確實是不支持寫返回值的
匿名函數(shù)和 lambda
匿名函數(shù)和 lambda都是屬于函數(shù)類型的對象, 你能怎么使用雙冒號加函數(shù)名,就能怎么使用匿名函數(shù)以及l(fā)ambda。Kotlin 的 lambda 和 Java8 的lambda 是不一樣的, Java8 的lambda 只是一種便捷寫法,本質(zhì)上沒有功能的突破,只是換了一種寫法, 而 Kotlin 的 lambda 是實實在在的對象,
總結(jié)
Kotlin 存在一種 Java 里不存在的類型,叫做 「函數(shù)類型」,這一類的函數(shù)類型的對象,在可以用來當(dāng)函數(shù)來使用的同時,還能作為函數(shù)的參數(shù)、函數(shù)的返回值以及賦值給變量。
創(chuàng)建一個函數(shù)類型的對象有3種方式
- 雙冒號加函數(shù)名
- 匿名函數(shù)
- Lambda
這幾種本質(zhì)都是函數(shù)類型的對象, 在 Kotlin 中 匿名函數(shù)不是函數(shù)