谈谈Java/Kotlin中接口回调
在谈接口回调之前,想什么是接口?在学习类的时候,我们就得知接口主要是用来把某个功能抽象起来,之后,不同的类实现此功能,但其实现的功能不同。
一个生动的例子,把动物的叫这一行为抽象为一个接口,之后不同动物由不同的类代替,这些动物类都实现了这个“叫”接口,但是,他们实现不一样,因为每个动物的叫声都不一样
上面所述为接口的基本定义和功能,接口除了上面的功能外,还可以用来回传数据,也就是所谓的接口回调,即本文谈论的主题
接口回调应用情形
接口回调主要是将数据/状态回传给调用者,举个常见的例子,下载功能
我们定义一个下载的类,此类存在一个download方法,我们只要实例化此类,用对象的调用方式来调用download方法,即可开始下载
问题来了,我们想要得到下载完成这个状态,那么该如何实现呢?
大家可能想到的是一种较为简单的方法,使用返回值来判断当前是否完成,给download方法声明返回值,下载完毕之后返回true
上面的方法可行是可行,但是如果需要得到多个数据呢?如下载完成之后需要返回一个File对象,下载失败则是返回错误信息Exception,按照上面的逻辑的话,你得定义两个类,用来存储数据?然后根据不同的情况返回不同的对象?但是返回值的对象只能是一种类型,你两个类该怎么办?
于是呢,我们可以选择接口回调,可以比较方便的实现上述的问题。
class DownloadUtil { fun download(downloadListener: DownloadListener) { try { //下载操作... //一般下载操作肯定是有个File对象进行读写操作,这里我就省略了 downloadListener.success(file) } catch (e: IOException) { downloadListener.error(e) } } interface DownloadListener { fun success(file: File) fun error(e: Exception) } }
可以看到,DownloadListener有两个接口方法,success()和error(),当调用者调用的时候,传递了一个接口对象,之后在各自不同的方法就可以处理不同两种情况的逻辑了
val downloadUtil = DownloadUtil() downloadUtil.download(object :DownloadUtil.DownloadListener{ override fun success(file: File) { //下载成功的相关操作 } override fun error(e: Exception) { //下载失败的相关操作 } })
Kotlin优雅的接口回调
Kotlin与Java不同的是,如果某个接口有一个方法的话,可以使用Kotlin中的接口参数
这里就谈谈如何使用Kotlin优雅的实现一个接口多方法
上面下载的接口我们是定义了两个方法,一个是下载成功,另外是下载失败,在实现接口的时候,我们必须两个方法都实现,哪怕你不关心某个方法,你都要实现,但方法里面不写任何代码,如果接口方法多的话,简直是地狱
Kotlin的object关键字就是相当于Java中用new来实现接口方法
那么有没有可以自由选择实现某个接口方法的办法呢?当然有,那就是Kotlin中提供的DSL语法
我们把上面的用Kotlin优雅的重构一遍
1.接口构造类
我们需要把接口改造成接口的构造类,如下代码所示
inner class ListenerBuilder { internal var successListener: ((file: File) -> Unit)? = null internal var errorListener: ((e: Exception) -> Unit)? = null //这里的方法名之后需要调用 fun onSuccess(action: (file: File) -> Unit) { successListener = action } fun onError(action: (e: Exception) -> Unit) { errorListener = action } } 对比: interface DownloadListener { fun success(file: File) fun error(e: Exception) }
fun success(file: File) 改造为接口参数((file: File) -> Unit),如果有返回值,把Unit改成对应返回值类型即可
2.提供方法传递接口构造类
private lateinit var mListener: ListenerBuilder fun download(listener: ListenerBuilder.() -> Unit) { mListener = ListenerBuilder().also(listener) try { //下载操作... if (::mListener.isInitialized) { mListener.successListener?.invoke(File("")) } } catch (e: IOException) { if (::mListener.isInitialized) { mListener.errorListener?.invoke(e) } } }
PS:这里你也可以选择声明另外的方法用于单独设置接口,之后调用逻辑就可以不需要传递接口参数过来,这个看你喜欢吧
3.调用
val downloadUtil = DownloadUtil()
downloadUtil.download{ //这里onSuccess和onError可选,IDE不会报错 onSuccess {
}
onError {
}
}
下载类源码
class DownloadUtil { private lateinit var mListener: ListenerBuilder fun registerListener(listener: ListenerBuilder.() -> Unit) { mListener = ListenerBuilder().also(listener) } fun download(listener: ListenerBuilder.() -> Unit) { mListener = ListenerBuilder().also(listener) try { //下载操作... if (::mListener.isInitialized) { mListener.successListener?.invoke(File("")) } } catch (e: IOException) { if (::mListener.isInitialized) { mListener.errorListener?.invoke(e) } } } inner class ListenerBuilder { internal var successListener: ((file: File) -> Unit)? = null internal var errorListener: ((e: Exception) -> Unit)? = null //这里的方法名之后需要调用 fun onSuccess(action: (file: File) -> Unit) { successListener = action } fun onError(action: (e: Exception) -> Unit) { errorListener = action } } }#Java##程序员#