
引言最近在写自己的日志库。设计网络模块和日志模块解耦的时候遇到了一个很自然的问题网络请求完成后怎么通知日志模块第一反应很简单定义一个 Interface。interface NetworkEventListener { fun onNetworkEvent(event: NetworkLogEvent) }网络模块产生事件listener?.onNetworkEvent(event)日志模块决定怎么处理。写到这里突然想到Kotlin 不是有 Lambda 和高阶函数吗是不是也可以这样var callback: ((NetworkLogEvent) - Unit)? null callback?.invoke(event)好像也完全没问题。继续往下想Interface、Listener、Callback、匿名内部类、Lambda、高阶函数、invoke……突然发现这些年学过的很多东西看起来完全不同但实际上一直在解决同一个问题。那就是如何把一段行为交给别人并在合适的时候执行。于是我试着从 Interface 开始把 Java 和 Kotlin 的这条回调设计路线重新串了一遍。一、最开始Interface刚学 Java 时大概都写过这样的代码interface Animal{ void eat(); } class Dog implements Animal{ Override public void eat(){ System.out.println(eat); } }老师会告诉我们抽象多态面向接口编程。工作以后又会写很多这样的接口interface LogUploader{ fun upload(logs: ListLog) }或者interface Cache{ fun save() fun load() }这些接口表达的是我需要一种能力。例如logUploader.upload(logs)意思就是请你帮我上传日志。Cachecache.save()意思就是请你帮我缓存数据。这是我们最熟悉的 Interface。二、另一种 Interface事件通知最近写日志库的时候又定义了一个接口interface NetworkEventListener{ fun onNetworkEvent(event: NetworkLogEvent) }这个接口和 LogUploader 有什么区别仔细想想LogUploader请你帮我做一件事情。NetworkEventListener我通知你发生了一件事情。例如listener?.onNetworkEvent(event)表示网络请求完成了。通知外部。Android 到处都是这种接口OnClickListener TextWatcher LocationListener SensorEventListener例如button.setOnClickListener(...)Button 并不是说请你帮我点击。而是在说我被点击了。通知你一下。原来Interface 不仅可以表达能力。也可以表达事件。而这两种场景我以前都会写却一直没有放在一起理解。三、Java 是怎么做回调的为了理解这件事情我们先看看最传统的 Java 回调。定义接口public interface NetworkEventListener { void onNetworkEvent(NetworkLogEvent event); }网络模块public class NetworkClient { private NetworkEventListener listener; public void setNetworkEventListener( NetworkEventListener listener ){ this.listener listener; } public void request(){ NetworkLogEvent event new NetworkLogEvent( /login, 200, 120 ); if(listener ! null){ listener.onNetworkEvent(event); } } }外部networkClient.setNetworkEventListener( new NetworkEventListener() { Override public void onNetworkEvent( NetworkLogEvent event ) { logger.write(event); } } );仔细看。其实就是定义 Interface。传进去。以后某个时机执行。这就是 Callback。四、Kotlin 的 Interface 回调Kotlin 写法更简单interface NetworkEventListener{ fun onNetworkEvent( event: NetworkLogEvent ) }网络模块class NetworkClient{ var listener: NetworkEventListener? null fun request(){ val event NetworkLogEvent( /login, 200, 120 ) listener?.onNetworkEvent(event) } }使用networkClient.listener object : NetworkEventListener{ override fun onNetworkEvent( event: NetworkLogEvent ) { logger.write(event) } }虽然 Kotlin 语法简单了。但是本质没有变化。还是Interface。Callback。事件通知。五、Lambda 和高阶函数写到这里突然想到Kotlin 不是可以直接这样吗网络模块class NetworkClient{ var onNetworkEvent: ((NetworkLogEvent)-Unit)? null fun request(){ val event NetworkLogEvent( /login, 200, 120 ) onNetworkEvent?.invoke(event) } }使用networkClient.onNetworkEvent { event - logger.write(event) }突然发现Interfacelistener?.onNetworkEvent(event)高阶函数onNetworkEvent?.invoke(event)看起来已经很接近了。本质上都是把一段逻辑交给别人。以后再执行。六、Interface 和高阶函数怎么选如果只有一个回调例如网络请求完成。扫码成功。登录成功。高阶函数非常舒服var onSuccess: ((User)-Unit)? null但是如果事件越来越多interface NetworkEventListener{ fun onRequestStart() fun onRequestSuccess() fun onRequestFailed() fun onRequestFinished() }会更加清晰。如果全部用高阶函数class NetworkClient( val onStart:(()-Unit)?, val onSuccess: ((NetworkLogEvent)-Unit)?, val onFailed: ((Throwable)-Unit)?, val onFinished: (()-Unit)? )当然也能写。但是组织性不如 Interface。所以高阶函数并不是取代 Interface。而是在单回调场景下把 Interface 写得更轻量。七、最近写库最大的收获最近设计日志库网络模块产生事件。日志模块消费事件。第一反应定义 Interface。后来想到是不是可以用高阶函数继续想Listener。Callback。匿名内部类。Lambda。invoke。突然发现以前总觉得Interface 是 Interface。Listener 是 Listener。Callback 是 Callback。Lambda 是 Lambda。高阶函数是高阶函数。最近做项目的时候才发现它们之间一直都有联系。虽然语法不同。虽然时代不同。虽然 Java 和 Kotlin 的写法不同。但是很多时候它们都在解决同一个问题。如何把一段行为交给别人并在未来某个时机执行。总结最近写日志库最大的收获并不是把日志框架写出来了。而是突然发现这些年学过的InterfaceListenerCallback匿名内部类Lambda高阶函数invoke。并不是一堆零散的知识。它们一直都在那里只是以不同的形式出现。以前学 Java学的是 Interface。后来做 Android学的是 Listener。再后来学 Kotlin学的是 Lambda 和高阶函数。直到最近设计自己的库才发现Interface 可以暴露能力logUploader.upload(logs)也可以通知事件listener.onNetworkEvent(event)高阶函数callback.invoke(event)只是另一种更加轻量的回调形式。它们虽然写法不同但很多时候都在完成同一件事情把一段行为交给别人并在合适的时候执行。或许对于一个程序员来说从「会写这些代码」到「知道它们为什么这样设计」本身就是成长的一部分。