news 2026/6/16 9:47:01

从 Interface 到 Lambda:一次串起 Java 和 Kotlin 的回调设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从 Interface 到 Lambda:一次串起 Java 和 Kotlin 的回调设计

引言

最近在写自己的日志库。

设计网络模块和日志模块解耦的时候,遇到了一个很自然的问题:

网络请求完成后,怎么通知日志模块?

第一反应很简单:

定义一个 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: List<Log>) }

或者:

interface Cache{ fun save() fun load() }

这些接口表达的是:

我需要一种能力。

例如:

logUploader.upload(logs)

意思就是:

请你帮我上传日志。

Cache:

cache.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) }

突然发现:

Interface:

listener?.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 的写法不同。

但是很多时候,它们都在解决同一个问题。

如何把一段行为交给别人,并在未来某个时机执行。


总结

最近写日志库,最大的收获并不是把日志框架写出来了。

而是突然发现:

这些年学过的:

  • Interface;

  • Listener;

  • Callback;

  • 匿名内部类;

  • Lambda;

  • 高阶函数;

  • invoke。

并不是一堆零散的知识。

它们一直都在那里,只是以不同的形式出现。

以前学 Java,学的是 Interface。

后来做 Android,学的是 Listener。

再后来学 Kotlin,学的是 Lambda 和高阶函数。

直到最近设计自己的库,才发现:

Interface 可以暴露能力:

logUploader.upload(logs)

也可以通知事件:

listener.onNetworkEvent(event)

高阶函数:

callback.invoke(event)

只是另一种更加轻量的回调形式。

它们虽然写法不同,但很多时候都在完成同一件事情:

把一段行为交给别人,并在合适的时候执行。

或许,对于一个程序员来说,

从「会写这些代码」,

到「知道它们为什么这样设计」,

本身就是成长的一部分。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/16 9:46:56

本地跑大模型实操指南:Ollama+LM Studio+Open WebUI部署全流程

1. 为什么“自己电脑跑AI”不是玄学&#xff0c;而是今天就能动手的日常操作&#xff1f;“自己电脑跑AI&#xff1f;”——去年这时候我听到这句话&#xff0c;第一反应是&#xff1a;这得是顶配工作站显卡堆叠散热塔吧&#xff1f;结果上个月&#xff0c;我用一台2020款MacBo…

作者头像 李华
网站建设 2026/6/16 9:42:09

GaussDB TPDSS一站式部署指南:从环境准备到连接测试

1. 项目概述&#xff1a;TPDSS是什么&#xff0c;以及为什么你需要它 如果你正在接触GaussDB数据库&#xff0c;无论是进行日常运维、数据迁移还是应用开发&#xff0c;大概率会听到“TPDSS”这个名字。作为一个在数据库领域摸爬滚打了十多年的老手&#xff0c;我见过太多因为工…

作者头像 李华
网站建设 2026/6/16 9:40:07

Qwen3智能体架构解析:从语言模型到可执行操作的范式跃迁

1. 项目概述&#xff1a;Qwen3不是一次简单升级&#xff0c;而是智能体时代的基础设施重装阿里通义千问宣布更新旗舰版Qwen3模型——这句话在技术圈刷屏时&#xff0c;我正蹲在一台RTX 3090工作站前调试ComfyUI的节点流。屏幕右下角弹出的新闻推送标题很短&#xff0c;但背后的…

作者头像 李华
网站建设 2026/6/16 9:40:07

Windows右键菜单深度定制指南:ContextMenuManager创新管理方案

Windows右键菜单深度定制指南&#xff1a;ContextMenuManager创新管理方案 【免费下载链接】ContextMenuManager &#x1f5b1;️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是否曾为Windows右键菜单的臃肿不堪…

作者头像 李华
网站建设 2026/6/16 9:30:58

OpenClaw Windows 安装配置指南与飞书控制

ps 文章环境仅在实验环境配置&#xff0c;无安全配置&#xff0c;切勿在生产环境中配置 一、环境准备 文章环境仅在实验环境配置&#xff0c;无安全配置&#xff0c;切勿在生产环境中配置 1. 安装 Node.js OpenClaw 由 JavaScript 语言编写&#xff0c;Node.js 是让 JS 程序…

作者头像 李华
网站建设 2026/6/16 9:28:53

Python filter() 惰性过滤与真值净化原理详解

1. 为什么我至今还在用filter()&#xff0c;而不是一上来就写列表推导式&#xff1f;在 Python 数据处理的日常里&#xff0c;“筛数据”这件事几乎每天都在发生&#xff1a;从日志里挑出错误行&#xff0c;从用户列表中找出活跃用户&#xff0c;从传感器读数中剔除异常值&…

作者头像 李华