什麼是高階函數?
高階函數是將函數作爲參數或者返回值的函數。
-
特點
- 參數可以是函數
- 返回值可以是函數
- 所有函數類型都有一個圓括號括起來的參數類型列表及一個返回類型
-
舉個🌰
var onClick:()->Util //onClick變量類型爲:無參且無返回類型的函數 var onClick:(A,B)->C //onCick變量類型爲:參數類型分別爲A,B,返回類型爲C的函數 (x:Int,y:Int) //函數類型可以選擇性包含函數的參數名,用於表明參數含義 ((Int,Int)->Util)? //如果函數類型可爲空,要使用圓括號後加?
上述就是一個高階函數用法,定義某個變量的類型爲函數,
注意:
- 參數類型可以爲空
()
Unit
返回類型不可省略
- 參數類型可以爲空
-
函數類型實例調用
函數類型的值可以通過其
invoke(...)
操作符調用:f.invoke(x)
或者直接f(x)
Lambda 表達式
-
簡述
- lambda 表達式總是被大括號括着,
- 其參數(如果有的話)在
—>
之前聲明(參數類型可以省略), - 函數體(如果存在的話)在
—>
後面。 - lambda隱式返回最後一個表達式的值(或者使用
限定的返回
語法顯示返回一個值)
-
語法
val sum:(Int,Int)->Int={ x:Int,y:Int->x+y}
在 Kotlin 中有一個約定,如果函數的最後一個參數是一個函數,並且你傳遞一個 lambda 表達式作爲相應的參數,你可以在圓括號之外指定它:
比如map函數
//map函數最後一個參數是一個函數
fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
val result = arrayListOf<R>()
for (item in this)
result.add(transform(item))
return result
}
//使用
val dataList = list.map{it—>
it*2
}
使用Kotlin高階函數實現接口回調
在學習使用Kotlin高階函數實現接口回調之前,先看看我們在Java中接口回調通常使用方法
Java中接口使用
-
普通使用
定義接口
public interface ICallback { void nextFun(String message); void errorFun(int code, String message); }
業務中使用
public class LoginLogicImpl { //聲明login方法,參數爲ICallback回調接口類型 public void login(ICallback callback) { //TODO... if (true) { callback.nextFun("success"); } else { callback.errorFun(404, "not found page"); } } }
外部調用(通過匿名內實現接口的回調)
public class LoginLogicPresenter { LoginLogicImpl loginLogic = new LoginLogicImpl(); public void login() { loginLogic = new LoginLogicImpl(); /** *調用LoginLogicImpl的login方法,通過new ICallback()匿名類方式實現接口回調。 *必須顯示聲明所有接口中的回調方法。 */ loginLogic.login(new ICallback() { @Override public void nextFun(String message) { Log.d(TAG,"message->" + message) } @Override public void errorFun(int code, String message) { Log.e(TAG,"code->" + code + "message->" + message) } }); } }
如上我們在使用該接口時候,需要顯示的聲明接口內所有的回調方法,如果不需要顯示聲明所有藉口回調方法怎麼辦呢? 那麼我們可以通過提供一個該接口的實現類來達到需求,具體實現如下
-
簡單使用
聲明接口的實現類
public class CallbackImpl implements ICallback { @Override public void nextFun(String message) { } @Override public void errorFun(int code, String message) { } }
業務中使用
public class LoginLogicImpl { //聲明函數,參數爲回調接口的實現類 public void register(CallbackImpl callbackImpl){ //TODO... if(true){ callbackImpl.nextFun("success"); }else{ callbackImpl.errorFun(404,"not found page"); } } }
外部調用
public class LoginLogicPresenter { LoginLogicImpl loginLogic = new LoginLogicImpl(); public void register() { /** *調用LoginLogicImpl的register方法,通過new CallbackImpl()匿名類 *實現回調(可選擇性的顯示聲明接口內回調方法) */ loginLogic.register(new CallbackImpl() { @Override public void nextFun(String message) { super.nextFun(message); Log.d(TAG,"success->" + message); } //@Override 可根據需要選擇性的顯示回調方法 //public void errorFun(int code, String message) { // super.errorFun(code, message); //} }); } }
Kotlin中接口使用
在kotlin中如果我們按照Java的思想進行接口開發使用,大概會有如下實現
-
Kotlin中接口普通使用
定義接口
interface ICallback { fun nextFun(message: String) fun errorFun(code: Int, message: String) }
業務中使用
object LoginLogicImpl { //聲明login方法,參數爲:ICallback回調接口類型 fun login( callback: ICallback) { //todo... if (true) { callback.nextFun("success") } else { callback.errorFun(400, "failure...") } } }
外部使用(使用
object對象表達式
,類似於java中的匿名類
)class LoginLogicPresenter { fun login() { /** *調用LoginLogicImpl的login方法,使用object對象表達式實現接口回調(必須要顯示聲明接口中 *全部回調方法) */ LoginLogicImpl.login(object : ICallback { override fun nextFun(message: String) { println("success->$message") } override fun errorFun(code: Int, message: String) { println("failure->$code,$message") } }) } }
上面就是我們在kotlin中通過java思想實現的接口,使用了object對象表達式(類似Java中匿名類) 實現回調。
在我們使用Android系統提供的View的
setOnClickListener
方法時候,我們發現其除了提供object
對象表達式方式之外,還提供了setOnClickListener(it->)
方法,如下button.setOnClickListener(object :View.OnClickListener{ override fun onClick(v: View?) { //TODO } })
button.setOnClickListener {it-> //TODO }
可見後者並沒有使用
object
對象表達式,而是通過kotlin的高階函數
和Lambda
表達式實現。那麼我們是不是也可以將我們之前的接口更改爲此種方式呢?當然可以!!如下: -
使用Kotlin高級函數和Lambda優化接口回調
-
定義接口:聲明回調函數
/** *定義回調接口 */ interface IResponseCallback<T> { fun nextFun(data: T?) fun errorFun(code: Int, message: String) }
-
定義接口實現類:聲明函數類型變量和對外提供的回調方法(與接口回調功能保持一致),並實現接口內回調方法
-
聲明函數類型變量(參數與接口回調函數一致)
class ResponseCallbackListener<T> : IResponseCallback<T> { //通過高階函數聲明函數類型變量(參數與接口互調函數一致) lateinit var next: (data: T?) -> Unit lateinit var error: (code: Int, message: String) -> Unit }
-
聲明回調方法(要與接口提供回調功能一致)
class ResponseCallbackListener<T> : IResponseCallback<T> { //通過高階函數聲明函數類型變量(參數與接口互調函數一致) lateinit var next: (data: T?) -> Unit lateinit var error: (code: Int, message: String) -> Unit //聲明回調方法(與接口回調功能一致),參數爲對應的函數類型變量 fun OnNext(nextListener: (data: T?) -> Unit) { this.next = nextListener } fun OnError(errorListener: (code: Int, message: String) -> Unit) { this.error = errorListener } }
-
實現接口內回調方法
class ResponseCallbackListener<T> : IResponseCallback<T> { //通過高階函數聲明函數類型變量(參數與接口互調函數一致) lateinit var next: (data: T?) -> Unit lateinit var error: (code: Int, message: String) -> Unit //聲明回調方法(與接口回調功能一致),參數爲對應的函數類型變量 fun OnNext(nextListener: (data: T?) -> Unit) { this.next = nextListener } fun OnError(errorListener: (code: Int, message: String) -> Unit) { this.error = errorListener } //實現接口內回調方法,調用對應函數變量的invoke(param)方法實現回調 override fun nextFun(data: T?) { //函數類型的值可以通過其 invoke(……) 操作符調用:f.invoke(x) 或者直接 f(x)。 this.next.invoke(data) //this.next(data) } override fun errorFun(code: Int, message: String) { this.error.invoke(code, message) } }
-
-
-
使用
業務中使用
object LoginLogicImpl { //聲明register方法,參數爲:函數類型 fun register(callback: CallbackListenerImpl.() -> Unit) { var callBack = CallbackListenerImpl() //實例化回調接口實現類 callBack.callback() //將參數內的回調函數與實例化對象綁定 if (true) { callBack.nextFun("success") //成功回調 } else { callBack.errorFun(400, "failure...") //失敗回調 } } }
外部調用
class LoginLogicPresenter { fun register() { //調用LoginLogicImpl的register方法(可選擇性顯示聲明回調方法) LoginLogicImpl.register { OnNext { println("OnNext->$it") } //OnError { code, message -> // println("OnError->$code,$message") //} } } }
上面使用Kotlin的高階函數以及Lambda表達式對普通的接口回調進行優化,並沒使用
object對象表達式
實現了接口回調,並且不需要顯示聲明接口提供的所有回調方法,而是選擇性的聲明所需的回調方法即可。
更多Kotlin高階函數及Lambda表達式學習地址參見《Kotlin中文社區》