谈谈Java/Kotlin中接口回调

Stars-one 2020年09月04日 156次浏览 本篇字数为0字

本文为作者原创,转载请注明出处,谢谢配合
作者:Stars-one
链接:https://stars-one.site/2020/09/04/谈谈javakotlin中接口回调


在谈接口回调之前,想什么是接口?在学习类的时候,我们就得知接口主要是用来把某个功能抽象起来,之后,不同的类实现此功能,但其实现的功能不同。

一个生动的例子,把动物的叫这一行为抽象为一个接口,之后不同动物由不同的类代替,这些动物类都实现了这个“叫”接口,但是,他们实现不一样,因为每个动物的叫声都不一样

上面所述为接口的基本定义和功能,接口除了上面的功能外,还可以用来回传数据,也就是所谓的接口回调,即本文谈论的主题

接口回调应用情形

接口回调主要是将数据/状态回传给调用者,举个常见的例子,下载功能

我们定义一个下载的类,此类存在一个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
        }
    }

}

参考

相关标签