lambda 和 高階函數(shù)
之前學(xué)習(xí)了 lambda 和高階函數(shù),然后在 android 開發(fā)中對 onClick 事件進(jìn)行監(jiān)聽是一個很常用的功能徽鼎,kotlin 的常規(guī)實(shí)現(xiàn)如下:
rootView.setOnClickListener { view ->
println("點(diǎn)擊了這個ID=${view.id}的view")
}
然后在開發(fā)中不可避免的我們也要寫一些自定義監(jiān)聽之類的代碼胚嘲。這個時候如果還用 java 的思想去實(shí)現(xiàn)的話就有點(diǎn)舍近求遠(yuǎn)了。
java 思想實(shí)現(xiàn)
在 java 中我們一般的做法是這樣的
- 定義一個接口
- 定義一個接口類型變量
- 定義一個 set 方法
- 調(diào)用 set 方法設(shè)置接口的實(shí)現(xiàn)類
用 kotlin 實(shí)現(xiàn)就是如下
class MyView{
//定義一個接口
interface IOnLabelCheckedListener {
fun onLabelCheck(label: String)
}
//定義一個接口類型變量
private lateinit var onLabelChecked: IOnLabelCheckedListener
private fun initView(context: Context) {
view.setOnCheckedChangeListener { radioGroup, i ->
if (checkListener()) {
//接口調(diào)用
onLabelChecked.onLabelCheck(radioGroup.findViewById<RadioButton>(i).text.toString())
}
}
}
private fun checkListener() = ::onLabelChecked.isInitialized
//定義一個 set 方法
fun setOnLabelCheckedListener(e: IOnLabelCheckedListener) {
this.onLabelChecked = e
}
}
// 調(diào)用set方法拘悦,通過匿名內(nèi)部類實(shí)現(xiàn)
MyView.setOnLabelCheckedListener(object : LabelBarView.IOnLabelCheckedListener {
override fun onLabelCheck(label: String) {
}
})
Java 實(shí)現(xiàn)的問題
當(dāng)然是太復(fù)雜了锋爪。同時可以稍微考慮一下性能的問題婆翔。而且最初的時候這樣寫一時搞不明白為什么 MyView.setOnLabelCheckedListener
方法內(nèi)部不能傳入 lambda 表達(dá)式水泉,lambda 表達(dá)式的存在不就是為了替代匿名內(nèi)部類嘛善涨。而且如果這個接口定義的是一個 java 類型的接口就是可以用 lambda 表達(dá)式的窒盐。這是為什么?最后猜想是因為 kotlin 在和 java 互相調(diào)用的時候中間又包裹了一層钢拧,而我們直接使用 kotlin 來定義這個接口不存在中間這一層就而我們定義的 set 方法又不是一個高階函數(shù)蟹漓,當(dāng)然不能使用 lambda 表達(dá)式。(其實(shí)這里參考 Java 的 lambda 的實(shí)現(xiàn)與匿名內(nèi)部類的區(qū)別也可以明白—— lambda 的內(nèi)部實(shí)現(xiàn)并不是匿名內(nèi)部類)
下面就用 kotlin 的思想來實(shí)現(xiàn)回調(diào)
使用高階函數(shù)來實(shí)現(xiàn)
kotlin 和 java 有一個重要的不同就是函數(shù)式編程娶靡。在函數(shù)式編程的思想中函數(shù)式一等公民牧牢,在使用 kotlin 時我們要多利用這種思維來思考問題看锉。Kotlin 中提供了高階函數(shù)姿锭,它可以直接使用一個函數(shù)來作為返回值,對于習(xí)慣于 java 來編程的我來說剛開始理解起來有些困難伯铣,下面我把我一步一步的實(shí)現(xiàn)一個高階函數(shù)的思路寫下呻此,希望對大家有所幫助。
首先腔寡,能想到的就是函數(shù)傳遞焚鲜,要用 lambda 來替代掉匿名內(nèi)部類可以這樣來實(shí)現(xiàn)
//從最基礎(chǔ)的開始做,把匿名內(nèi)部類通過 lambda 實(shí)現(xiàn)
MyView.setOnLabelCheckedListener(object : MyView.IOnLabelCheckedListener {
override fun onLabelCheck(label: String) {
println(label)
}
})
// 首先 MyView.IOnLabelCheckedListener 中只有一個方法 onLabelCheck(label: String)
// 因此可以寫出 lambda 表達(dá)式如下
var lam: (String) -> Unit = { label -> println(label) }
然后放前,需要把寫好的 lambda 傳遞進(jìn)去忿磅,這個時候就要求 setOnLabelCheckedListener
方法是一個高階函數(shù)
// 這里接收一個 上面我們改造好的表達(dá)式 lam ,它內(nèi)部實(shí)現(xiàn)應(yīng)該是把 e 賦值給當(dāng)前類的一個對象
fun setOnLabelCheckedListener(e: (String) -> Unit) {
this.lisenter = e
}
//顯然 lisenter 就應(yīng)該是這樣的
lateinit var linsnter: (String) -> Unit
最后使用 linsnter 進(jìn)行回調(diào)
private fun initView(context: Context) {
view.setOnCheckedChangeListener { radioGroup, i ->
linsnter(radioGroup.findViewById<RadioButton>(i).text.toString())
}
}
最終代碼結(jié)果:
class MyView{
lateinit var linsnter: (String) -> Unit
private fun initView(context: Context) {
view.setOnCheckedChangeListener { radioGroup, i ->
if (checkListener())
linsnter(radioGroup.findViewById<RadioButton>(i).text.toString())
}
}
private fun checkListener() = ::linsnter.isInitialized
fun setOnLabelCheckedListener(e: (String) -> Unit) {
this.lisenter = e
}
}
// 調(diào)用時將變量 lam 省略,直接使用一個表達(dá)式
view.setOnLabelCheckedListener { label ->
println(label)
}
最終的代碼和之前的代碼有兩個最大的不同凭语,一是沒有了接口定義葱她,二是沒有了匿名內(nèi)部類。
這樣的好處是顯而易見的似扔,因為少了匿名內(nèi)部類的使用吨些,在程序運(yùn)行過程中是提高了性能的。
以上就是在 Kotlin 中使用高階函數(shù)來替代傳統(tǒng)的回調(diào)函數(shù)的方法炒辉。不對之處還請指正豪墅。