在谈接口回调之前,想什么是接口?在学习类的时候,我们就得知接口主要是用来把某个功能抽象起来,之后,不同的类实现此功能,但其实现的功能不同。
一个生动的例子,把动物的叫这一行为抽象为一个接口,之后不同动物由不同的类代替,这些动物类都实现了这个“叫”接口,但是,他们实现不一样,因为每个动物的叫声都不一样
上面所述为接口的基本定义和功能,接口除了上面的功能外,还可以用来回传数据,也就是所谓的接口回调,即本文谈论的主题
接口回调应用情形
接口回调主要是将数据/状态回传给调用者,举个常见的例子,下载功能
我们定义一个下载的类,此类存在一个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优雅地设计lambda接口参数 | Stars-One的杂货小窝,这里不再赘述
这里就谈谈如何使用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
}
}
}
评论区