Kotlin 使用高階函數(shù)實(shí)現(xiàn)回調(diào)方式
lambda 和 高階函數(shù)
之前學(xué)習(xí)了 lambda 和高階函數(shù),然后在 android 開發(fā)中對(duì) onClick 事件進(jìn)行監(jiān)聽是一個(gè)很常用的功能,kotlin 的常規(guī)實(shí)現(xiàn)如下:
rootView.setOnClickListener { view ->
println("點(diǎn)擊了這個(gè)ID=${view.id}的view")
}
然后在開發(fā)中不可避免的我們也要寫一些自定義監(jiān)聽之類的代碼。這個(gè)時(shí)候如果還用 java 的思想去實(shí)現(xiàn)的話就有點(diǎn)舍近求遠(yuǎn)了。
java 思想實(shí)現(xiàn)
在 java 中我們一般的做法是這樣的
定義一個(gè)接口
定義一個(gè)接口類型變量
定義一個(gè) set 方法
調(diào)用 set 方法設(shè)置接口的實(shí)現(xiàn)類
用 kotlin 實(shí)現(xiàn)就是如下
class MyView{
//定義一個(gè)接口
interface IOnLabelCheckedListener {
fun onLabelCheck(label: String)
}
//定義一個(gè)接口類型變量
private var onLabelChecked: IOnLabelCheckedListener? = null
private fun initView(context: Context) {
view.setOnCheckedChangeListener { radioGroup, i ->
onLabelChecked.onLabelCheck(radioGroup.findViewById<RadioButton>(i).text.toString())
}
}
//定義一個(gè) set 方法
fun setOnLabelCheckedListener(e: IOnLabelCheckedListener) {
this.onLabelChecked = e
}
}
// 調(diào)用set方法,通過(guò)匿名內(nèi)部類實(shí)現(xiàn)
MyView.setOnLabelCheckedListener(object : LabelBarView.IOnLabelCheckedListener {
override fun onLabelCheck(label: String) {
}
})
這樣實(shí)現(xiàn)的問題
當(dāng)然是太復(fù)雜了。而且最初的時(shí)候這樣寫一時(shí)搞不明白為什么 MyView.setOnLabelCheckedListener 方法內(nèi)部不能傳入 lambda 表達(dá)式,lambda 表達(dá)式的存在不就是為了替代匿名內(nèi)部類嘛。而且如果這個(gè)接口定義的是一個(gè) java 類型的接口就是可以用 lambda 表達(dá)式的。這是為什么?最后猜想是因?yàn)?kotlin 在和 java 互相調(diào)用的時(shí)候中間又包裹了一層,而我們直接使用 kotlin 來(lái)定義這個(gè)接口不存在中間這一層,而我們定義的 set 方法又不是一個(gè)高階函數(shù),當(dāng)然不能使用 lambda 表達(dá)式。
下面就用 kotlin 的思想來(lái)實(shí)現(xiàn)回調(diào)
使用高階函數(shù)來(lái)實(shí)現(xiàn)
kotlin 和 java 有一個(gè)重要的不同就是函數(shù)式編程。在函數(shù)式編程的思想中函數(shù)是一等公民,在使用 kotlin 時(shí)我們要多利用這種思維來(lái)思考問題。Kotlin 中提供了高階函數(shù),它可以直接使用一個(gè)函數(shù)來(lái)作為返回值,對(duì)于習(xí)慣于 java 來(lái)編程的我來(lái)說(shuō)剛開始理解起來(lái)有些困難,下面我把我一步一步的實(shí)現(xiàn)一個(gè)高階函數(shù)的思路寫下,希望對(duì)大家有所幫助。
首先,能想到的就是函數(shù)傳遞,要用 lambda 來(lái)替代掉匿名內(nèi)部類可以這樣來(lái)實(shí)現(xiàn)
//從最基礎(chǔ)的開始做,把匿名內(nèi)部類通過(guò) lambda 實(shí)現(xiàn)
MyView.setOnLabelCheckedListener(object : MyView.IOnLabelCheckedListener {
override fun onLabelCheck(label: String) {
println(label)
}
})
// 首先 MyView.IOnLabelCheckedListener 中只有一個(gè)方法 onLabelCheck(label: String)
// 因此可以寫出 lambda 表達(dá)式如下
var lam: (String) -> Unit = { label -> println(label) }
然后,需要把寫好的 lambda 傳遞進(jìn)去,這個(gè)時(shí)候就要求 setOnLabelCheckedListener 方法是一個(gè)高階函數(shù)
// 這里接收一個(gè) 上面我們改造好的表達(dá)式 lam ,它內(nèi)部實(shí)現(xiàn)應(yīng)該是把 e 賦值給當(dāng)前類的一個(gè)對(duì)象
fun setOnLabelCheckedListener(e: (String) -> Unit) {
this.lisenter = e
}
//顯然 lisenter 就應(yīng)該是這樣的
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{
var linsnter: (String) -> Unit = {}
private fun initView(context: Context) {
view.setOnCheckedChangeListener { radioGroup, i ->
linsnter(radioGroup.findViewById<RadioButton>(i).text.toString())
}
}
fun setOnLabelCheckedListener(e: (String) -> Unit) {
this.lisenter = e
}
}
// 調(diào)用時(shí)將變量 lam 省略,直接使用一個(gè)表達(dá)式
view.setOnLabelCheckedListener { label ->
println(label)
}
最終的代碼和之前的代碼有兩個(gè)最大的不同,一是沒有了接口定義,二是沒有了匿名內(nèi)部類。
更好的使用高階函數(shù)
高階函數(shù)的使用更多的時(shí)候能使我們的代碼更簡(jiǎn)潔,比如下面這段代碼:
fun refreshData(e: ((Boolean, String) -> Unit)): Boolean {
if (!UserInfoManager.getInstance().isLogin) {
e(false, "未登錄")
return false
}
NETWorkUtils.request(ApiParamter(), object : ApiListener<ResponseData> {
override fun onApiCompleted(data: ResponseData?) {
e(true, "成功")
}
override fun onApiError(errorCode: Int, errorCodeMessage: String) {
e(false, errorCodeMessage)
}
})
return true
}
那么在調(diào)用它的時(shí)候就可以這樣:
mView.refreshData { isSuccess, msg ->
//do something
}
是不是很簡(jiǎn)單,省去了再寫一個(gè)接口。同時(shí)如果是用 java 來(lái)調(diào)用 refreshData 方法也一樣可以的:
mView.refreshData(new Function2<Boolean, String, Unit>() {
@Override
public Unit invoke(Boolean aBoolean, String s) {
// do something
return null;
}
});
Kotlin 提供了一系列的 Function 接口類來(lái)供 java 調(diào)用高階函數(shù)時(shí)使用,最多支持22個(gè)參數(shù)有興趣的可以查看一下。
以上就是在 Kotlin 中使用高階函數(shù)來(lái)替代傳統(tǒng)的回調(diào)函數(shù)的方法。不對(duì)之處還請(qǐng)指正。希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Android Studio實(shí)現(xiàn)仿微信APP門戶界面詳解及源碼
這篇文章帶你通過(guò)Android studio來(lái)實(shí)現(xiàn)微信APP的門戶界面,主要說(shuō)明框架的各部分功能與實(shí)現(xiàn)過(guò)程,下文包含了整個(gè)開發(fā)過(guò)程,以及解決問題的思路并再末尾提供了源碼鏈接2021-10-10
Pagerslidingtabstrip菜單標(biāo)題欄制作方法
這篇文章主要為大家詳細(xì)介紹了Pagerslidingtabstrip菜單標(biāo)題欄的制作方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
Android中Fragment相互切換間不被回收的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android中Fragment相互切換間不被回收的實(shí)現(xiàn)方法,文中給出了詳細(xì)的示例代碼和注釋供大家參考學(xué)習(xí),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-08-08
Android評(píng)分RationBar控件使用詳解
這篇文章主要為大家詳細(xì)介紹了Android評(píng)分RationBar控件的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
Android Gradle Build Error:Some file crunching failed, see l
這篇文章主要介紹了Android Gradle Build Error:Some file crunching failed, see logs for details解決辦法的相關(guān)資料,需要的朋友可以參考下2016-11-11
Android實(shí)現(xiàn)pdf在線預(yù)覽或本地預(yù)覽的方法
下面小編就為大家分享一篇Android實(shí)現(xiàn)pdf在線預(yù)覽或本地預(yù)覽的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
Android 網(wǎng)絡(luò)html源碼查看器詳解及實(shí)例
這篇文章主要介紹了Android 網(wǎng)絡(luò)html源碼查看器詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03
Android里面的Service種類以及啟動(dòng)方式
Android中的Service分為前臺(tái)服務(wù)和后臺(tái)服務(wù),前臺(tái)服務(wù)需要亮身份牌并顯示通知,后臺(tái)服務(wù)則有啟動(dòng)方式選擇,包括startService和bindService,選擇啟動(dòng)方式應(yīng)根據(jù)任務(wù)類型和場(chǎng)景進(jìn)行,需要的朋友可以參考下2025-02-02
Android 仿微信自定義數(shù)字鍵盤的實(shí)現(xiàn)代碼
本篇文章主要介紹了Android 仿微信自定義數(shù)字鍵盤的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07

