用实际例子理解回调函数(Calback)

2023-11-19

用实际例子理解回调函数(Calback)

在我们编码的过程中,调用和回调几乎无处不在,但是我对回调函数到底是怎样一回事并没有一个真正透彻的理解,最近我查找学习了一些资料,学到了很多。
我参考了一些知乎上的分享,很不错。
https://www.zhihu.com/question/19801131

1 调用和回调的定义

1.1 调用

对于调用,相信大家都不陌生,在一个应用系统中,模块之间必然存在调用。
什么是调用?
如图1.1所示,类A中的方法a()调用类B的方法b(),方法b()执行完毕会传回结果给方法a(),方法a()会用到这个结果继续往下执行。
调用

下面通过一个场景来体会下调用,以Java为例。
有两个类Employee类和Boss类。
1.Employee类:可以看作一个工具类,它的doAnswer()方法,能够对传进来的任务参数“1+1=?”计算出答案。

public class Employee{
    public String doAnswer(String work){
        if ("1+1=?".equals(work)) {
            return 2;
        }
        else{
            return null;
    }
}

2.Boss类:他有一项任务“1+1=?”需要完成,包括计算出答案,然后提交答案。
Boss类调用Employee类的doAnswer()方法来计算出答案。
boss得到employee给出的答案,然后用这个答案完成自己的doWork()方法(也就是提交答案)。

public class Boss{
    public static void main(String[] args) {
        Boss boss=new Boss();
        String aWork="1+1=?";//任务
        Employee employee=new Employee(); 
        String theAnswer=employee.doAnswer(aWork);
        boss.doWork(aWork,theAnswer);
    }
    public void doWork(String work,String answer){
        if (answer!=null)) {
            System.out.println("答案:"+answer);
        }
        else{
            System.out.println("答案:"+"空白");
        }
    }
}

1.2 回调

那么什么是回调呢?
回调:如图类A的a()方法调用类B的b()方法,类B的b()方法执行完毕后,主动调用类A的callback()方法。这就是回调。
回调是一种双向的调用方式。
回调

知道了回调,我们再分析一下上边提到的那个调用的应用场景。
对于上边的调用,我们可以看出有些麻烦,employee计算出答案后,boss需要将答案抄写过来,然后提交答案。
为什么不能让employee计算出答案后,直接帮boss提交答案呢?所以这就要用到回调。

依旧是两个类Boss类和Employee类。
1.Boss类:boss依旧是这项任务“1+1=?”需要完成,包括计算出答案,然后提交答案。
Boss类调用Employee类的doAnswer()方法来完成这项任务。这次不仅将任务传给doAnswer()方法,而且将自己的引用也传给它,让doAnswer()方法计算出答案后,直接帮boss提交答案。
这就相当于boss将自己的任务和账号都告诉employee,直接让employee计算出答案后用boss的账号提交答案。

public class Boss{
    public static void main(String[] args) {
        Boss boss=new Boss();
        String aWork="1+1=?";
        Employee employee=new Employee();
        employee.doAnswer(aWork,boss);
    }
    public void doWork(String work,String answer){
        if (answer!=null)) {
            System.out.println("答案:"+answer);
        }
        else{
            System.out.println("答案:"+"空白");
        }
    }
}

2.Employee类:依旧是个工具类,但是它的doAnswer()方法有两个传入的参数了,包括任务参数和身份参数(Boss的引用)。
当计算出答案后,employee用boss的引用调用boss的doWork()方法,这就相当于employee帮boss提交了答案。
其中,boss的doWork()方法就是回调函数。

public class Employee{
    public String doAnswer(String work,Boss boss){
        if ("1+1=?".equals(work)) {
            boss.doWork(work,"2");//doWork是回调函数
        }
        else{
            boss.doWork(work,"空白");
    }
}

总结一下这个例子的流程:
(1)Boss完成任务的doWork()方法有两个入参:①任务内容,②任务答案。
(2)Employee为了帮Boss完成任务提供了一个doAnswer()方法,该方法有两个入参:①任务内容②Boss的引用。
(3)程序执行时,Boss只要调用Employee的doAnswer()方法就行了。因为一旦Employee计算出答案,会直接根据Boss的引用调用Boss的doWork()方法提交好答案。
抽象点描述:
类A调用类B的方法b(传入相关信息)完成一定的功能,类B的方法无法实现全部功能,需要反过头来调用类A的方法a来完成。
其中,方法a就是传说中的回调函数。
再抽象点描述:
调用(A调用B),回调(B调用A)。

通过上边逐步递进的思考,大家应该都应该知道回调函数是怎样一回事了。总结一下回调函数的实现原理:
(1)定义一个函数a;
(2)提供函数实现的一方A在初始化的时候,将函数a的函数指针注册给调用者B;
(3)当特定的事件或条件发生的时候,调用者B使用函数指针调用函数a对事件进行处理。
即把我要执行的这个任务写成一个函数,将这个函数和某一时间或者事件或者中断建立关联。当这个关联完成的时候,这个函数华丽的从普通函数变身成为回调函数。
所以我们可以知道:
①函数可以理解为一个功能体,执行它可以完成一个任务。回调函数就是一个函数,只是执行时间和执行主体与普通的函数稍有区别。
②使用回调函数本质上就是一种利用函数指针进行函数调用的过程。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
2 回调函数的改进——接口方式
我们分析一下上边回调中举的Boss和Employee的例子,可以看出有两点不妥:
1.常常使用回调函数的同学可能会说,没怎么见过在调用函数中直接把对象的引用写到第一次调用的方法里面的。确实,只是调用Employee类的方法完成任务,用得着把自己的全部暴露给别人吗?这样做是极其不安全的。
2.Employee类是工具类,用来解决Boss提出的任务,通过回调Boss的doWork()方法完成任务。如果有个工人也有相同的任务想让Empoloyee来解决,应该怎么办呢?难道再开个方法,专门接收工人的引用作为传参?如果还有警察也有相同任务,该怎么办?处理任务的就Employee,而任务提出者可以是各种各样的,难道对不同的角色都单独开一个方法吗?
所有的对象要有同一个方法,这也就引出了接口的概念。
一般的做法是将回调方法doWork()做成一个接口,不同的任务提出者去实现该接口,并把自己的接口实现类的对象在调用任务处理者时传给它。
那么不管你是老板、工人、警察等,都可以直接调用Employee类的doAnswer()方法来解决你的任务。

下面来看一下接口方式的回调函数的应用场景:
1.将回调函数抽象出来作为接口。这个接口的作用,仅仅是用来规定doWork()方法长什么样。

public interface DoWork{
    void  doWork(String work,String answer);
}

2.创建一个工人Worker类,在该类中实现上边的回调函数接口,那么就默认继承的doWork()方法。

public class Worker implements DoWork{
    public static void main(String[] args) {
        Worker worker=new Worker();
        String work="1+1=?";

        Employee employee=new Employee();
        employee.doAnswer(work,worker);
    }
    @override
    public void doWork(String work,String answer){
        if (answer!=null) {
            System.out.println("答案:"+answer);
        }
        else{
            System.out.println("答案:"+"空白");
        }
    }

}

3.Employee工具类
Empolyee不需要改动任何代码,就可以直接完成任务了。

public class Employee{
    public String doAnswer(String work,DoWork someone){
        if ("1+1=?".equals(work)) {
            someone.doWork(work,"2");
        }
        else{
            someone.doWork(work,"空白");
    }
}

3 回调函数的优缺点
3.1 优点
1.很多时候回调函数可以用来执行条件驱动的任务。即当该回调函数关心的那个条件被触发时,回调函数将被执行。条件触发可以是某一时间到了或者某一事件发生或者某一中断触发。

回调函数不是由该函数的实现方直接调用,而是在特定的条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

  1. 使程序设计更灵活。允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。而且这一设计允许了底层代码调用在高层定义的子程序。

3.回调函数最大的优势应用就是异步回调。
程序变成异步的了,也就是你不必再调用这个函数的时候一直等待这个时间的到达、事件的发生或中断的发生(万一一直不发生,你的程序会怎么样?)
在此期间你可以做别的事情,或者四处逛逛。当回调函数被执行时,你的程序重新得到执行的机会,此时你可以继续做必要的事情了。
3.2 缺点
1.学习成本会比普通函数高,需要有一定的抽象思维能力,需要对应用场景的理解。
2.回调函数很多情况下会附带有跨线程操作甚至于跨进程的操作,这些都是异步带来的成本。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用实际例子理解回调函数(Calback) 的相关文章

  • Python中 ''.JOIN()的用法

    Python join 方法 描述 将序列中的元素以指定的字符连接生成一个新的字符串 语法 语法 sep join seq 参数说明 sep 分隔符 可以为空 seq 要连接的元素序列 字符串 元组 字典 返回值 返回通过指定字符连接序列中
  • python 函数的基础操作,看完大家都可以创建函数 ^o^/

    目录 函数简介 函数返回结果的两种方法 形参的三种类型 必选参数 默认参数 不定长参数 函数返回值 return 内置函数 函数简介 在python里什么是函数 函数又被称为方法 是指某一段聚合在一起 做特定的事情的代码 创建一个函数需要哪
  • Caffe中 math_functions 分析

    本篇博客转载自 Caffe源码 一 math functions 分析 math function 定义了caffe 中用到的一些矩阵操作和数值计算的一些函数 这里以float类型为例做简单的分析 1 caffe cpu gemm temp
  • PHP数字金额转中文大写金额

    今天开发一个项目功能的时候突然需要将数字金额转换成大写中文金额 故在百度搜索了一翻 再针对性都修改一点点 记录此处 金额转汉字大写 function cny ns static cnums array 零 壹 贰 叁 肆 伍 陆 柒 捌 玖
  • 深度学习中30个关于数据的问题

    1 下面的数据是线性可分的嘛 否 2 下面哪个是通用的模型逼近器 A Kernel SVM B Neural Networks C Boosted Decision Trees D All of the above D 以上所有都可以得到一
  • javascript 贪心算法说明

    贪心算法 贪心算法遵循一种近似解决问题的技术 期盼通过每个阶段的局部最优选择 当前最好的解 从而达到全局的最优 全局最优解 最少硬币找零问题 最少硬币找零是给出要找零的钱数 以及可以用硬币的额度数量 找出有多少种找零方法 如 美国面额硬币有
  • 微信小程序无法获取头像,昵称的解决办法 (原生)

    最新发布的微信小程序已经无法获取头像和昵称 那么如何解决这个问题呢 其实很简单 新用户注册后 提示跳转到新页面 要求修改昵称和头像即可
  • 小程序跳转带参数

    携带参数 pages reg reg wx navigateTo url pages promise promise name pages reg reg 接收参数 onLoad function arg console log sssss
  • LintCode之128 哈希函数

    题目来源 哈希函数 题目描述 在数据结构中 哈希函数是用来将一个字符串 或任何其他类型 转化为小于哈希表大小且大于等于零的整数 一个好的哈希函数可以尽可能少地产生冲突 一种广泛使用的哈希函数算法是使用数值33 假设任何字符串都是基于33的一
  • 关键字static、Const、Volatile的作用是什么

    关键字static的作用是什么 在C语言中 关键字static有三个明显的作用 1 在函数体 一个被声明为静态的变量在这一函数被调用结束后不释放其存储空间 定义为static的局部变量的存储在全局区 静态区 而一般的局部变量存储在栈中 2
  • c++中setw()与setfill()的用法详情

    c 中setw 与setfill 的用法详情 在C 中 setw int n 用来控制输出间隔 例如 cout lt lt s lt
  • JS 数组求和的5种方法(解题报告)

    转自牛客网 题目 题目描述 计算给定数组 arr 中所有元素的总和 输入描述 数组中的元素均为 Number 类型 输入例子 sum 1 2 3 4 输出例子 10 不考虑算法复杂度 用递归做 1 2 3 4 5 6
  • 与MySQL的零距离接触(三.函数、存储过程、引擎、图形化管理工具)

    金山竹影几千秋 云索高飞水自流 万里长江飘玉带 一轮银月滚金球 远自湖北三千里 近到江南十六州 美景一时观不透 天缘有分画中游 祝大家小年快乐 2018福气生财 一 运算符和函数 准备 连接数据库 mysql uroot proot P33
  • C语言EasyX_2018中的putimage(x, y, w, h, img, x1, y1)函数

    putimage x y w h img x1 y1 函数一共有六个参数 以下解释来自EasyX 2018的帮助文件 这个函数的几个重载用于在当前设备上绘制指定图像 绘制图像 void putimage int dstX 绘制位置的 x 坐
  • 实验6-6 使用函数验证哥德巴赫猜想 (20分)

    http pta patest cn pta test 13 exam 3 question 478 include
  • Sybase IQ常用函数大全--杂项函数

    Sybase IQ常用函数大全 杂项函数 查询索引 COALESCE 函数 返回列表中的第一个非 NULL 表达式 IFNULL 函数 返回第一个非空值表达式或 NULL ISNULL 函数 返回参数列表中的第一个非 NULL 表达式的值
  • RxJava 事件流之聚合

    Aggregation 前面介绍了如何过滤掉不需要的数据 如何根据各种条件停止发射数据 如何检查数据是否符合某个条件 这些操作对数据流来说都是非常有意义的 本节介绍如何根据数据流中的数据来生成新的有意义的数据 本节的操作函数会使用源 Obs
  • 回调函数&&回调机制

    所谓回调 定义是 一个方法的指针传递给事件源 当某一事件发生时用来调用这个方法 比如客户程序C调用服务程序S中的某个函数A 然后S又在某个时候反过来调用C中的某个函数B 对于C来说 这个B便叫做回调函数 例如Win32下的窗口过程函数就是一
  • 为什么函数y=f(x)的导数dy/dx可以适用分数运算呢?

    一 问题背景 在同济大学高等数学关于导数的内容中 如果函数y f x 可以由参数方程 表示 且三个函数皆可导 且x的值不为0 则 才开始看这个公式推导时 觉得没什么问题 仔细一想 dy dx是导数的表示符号 为什么这个符号可以适用分数运算公
  • 【高等数学基础知识篇】——函数,极限与连续

    本文仅用于个人学习记录 使用的教材为汤家凤老师的 高等数学辅导讲义 本文无任何盈利或者赚取个人声望的目的 如有侵权 请联系删除 文章目录 一 函数基础知识 1 1 基本初等函数和初等函数 1 2 函数的初等特性 1 3 特殊函数 二 函数题

随机推荐

  • 高防cdn和高防服务器的区别,有什么不一样

    CDN通俗的理解就是网站加速 可以解决跨运营商 跨地区 服务器负载能力过低 带宽过少等带来的网站打开速度慢等问题 一个网站的服务器性能比较差 负载能力有限 优势面临突发流量 招架不住 直接导致服务器奔溃 网站打不开 CDN 跟 高防服务器
  • 快速玩转 Llama2!阿里云机器学习 PAI 推出最佳实践

    前言 近期 Meta 宣布大语言模型 Llama2 开源 包含7B 13B 70B不同尺寸 分别对应70亿 130亿 700亿参数量 并在每个规格下都有专门适配对话场景的优化模型Llama 2 Chat Llama2 可免费用于研究场景和商
  • DesktopUI与ZeroTierOne的数据交互机制分析

    分析源码 梳理了一个调用关系图
  • 用python绘制RC低通滤波器bode图

    用python绘制RC低通滤波器bode图 Bode图 Bode图 国内有译作 伯德图 也有译作 波特图 是一种用于描述线性系统的频率响应的图形工具 频率响应是指系统对不同频率的输入信号的响应程度 通常用幅度和相位来表示 Bode图以对数坐
  • layui复选框按钮事件(智能去重刷新)

    1 写好复选框 lt input type checkbox value 0 name available title 智能去重 id available lay filter available gt 2 给复选框加事件 form on
  • RMSE数值在什么范围比较好呢

    RMSE Root Mean Squared Error 数值越小越好 通常来说 对于大多数应用来说 RMSE的值在0 1到1之间是可以接受的 当然 这取决于具体的应用和数据 如果数据本身具有很大的方差 那么RMSE的值就会更大
  • 如何制作静态和动态链接库-小白入门

    1 gcc编译过程 gcc为GNU编译套件 GNU Compiler Colletion 2 gcc编译命令 0 o 指定生成目标文件 00 O 设定优化级别 123越大越高 1 I 指定头文件目录 2 D 指定宏 避免修改源代码 3 g
  • 《我的世界》Python编程入门(9) 使用函数建造房子

    一 函数的基本概念 1 1 函数在数学中的概念 函数指一个量随着另一个量的变化而变化 函数的数学形式 y f x f是一种定义好的关系 可以简称为函数 在函数f中 只要x值的确定 那么y的值一定是确定的 y的值随x值的变化而变化 1 2 P
  • 设计模式(5)-适配器模式(Adapter Pattern)

    适配器模式 Adapter Pattern 顾名思义 就像变压器 转接头差不多 就像美国的生活电压是110V 中国是220V 就需要一个变压器将220V转换成110V 或者一个Type C接口想插如USB接口的东西 你就需要一个转换器 而这
  • [附源码]JSP+ssm计算机毕业设计小区疫情物资配送管理系统624kg【源码、数据库、LW、部署】

    项目运行 项目含有源码 文档 程序 数据库 配套开发软件 软件安装教程 环境配置 Jdk1 8 Tomcat7 0 Mysql HBuilderX Webstorm也行 Eclispe IntelliJ IDEA Eclispe MyEcl
  • LeetCode题目笔记——2331. 计算布尔二叉树的值

    文章目录 题目描述 题目难度 简单 方法一 经典后序遍历 代码 C C Python 总结 题目描述 给你一棵 完整二叉树 的根 这棵树有以下特征 叶子节点 要么值为 0 要么值为 1 其中 0 表示 False 1 表示 True 非叶子
  • 【开源电机驱动】H 桥驱动-软件篇

    原文地址 http www modularcircuits com blog articles h bridge secrets h bridge control 本文为作者翻译校正稿件 含个人理解批注 H bridge Control H
  • 无需更改注册表 实现CHM文件从共享文件中直接打开

    直接上解决方法 无需更改注册表 将整个CHM文件压缩 在压缩文件中打开 chm文件 就可以正常显示相关内容 1 问题描述 压缩前 两台电脑 A是笔记本电脑 win10系统 B是台式电脑 win7系统 在A中设置了共享文件 并共享给了B CH
  • 别光看NB的Github开源项目,你得参考他们去设计自己的架构!

    V xin ruyuanhadeng获得600 页原创精品文章汇总PDF 一 背景引入 首先简单介绍一下项目背景 公司对合作商家提供一个付费级产品 这个商业产品背后涉及到数百人的研发团队协作开发 包括各种业务系统来提供很多强大的业务功能 同
  • React 从零开始学习(四)—— 组件交互

    上一节 实现了把一个 prop 从父组件 Board 传递 给了子组件 Square 在 React 应用中 数据通过 props 的传递 从父组件流向子组件 这点跟 vue 是一样的 然后 跟着教程给组件添加交互功能 给组件添加交互功能
  • 简单学习识谱(六线谱)

    简单学习识谱 六线谱 参考资料 简谱的记谱方法 参考资料 吉他自学三月通 简谱的记谱方法 乐谱就是叙述音乐语言的文字 是每个学习音乐的人必须掌握的学习工具 当今世界通用的记谱法有五线谱和简谱 这两种方法都有着各自的特点 五线谱对于记录多声部
  • STM32通用定时器使用详解

    1 通用定时器基本介绍 通用定时器包括TIM2 TIM3 TIM4和TIM5 STM32通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成 每个定时器都是完全独立的 没有互相共享任何资源 它们可以一起同步操作 定时器可以进行定
  • main.c(31): warning: #223-D: function “uart_init“ declared implicitly

    Keil5编程之warning 223 D function xxx declared implicitly 1 函数没有头文件中进行声明 在头文件中添加声明 2 定义错误 字母大小可能不一致 仔细看一下出现问题的函数是否在声明和调用时使用
  • C语言入门——适合练手的密码本项目

    一 引言 学C语言有一段时间了 趁着正好做了密码本的小项目 把它分享出来 二 思路与原理 密码本 见名知意 就是存放账号密码 起到备忘录作用的本子 将需要备忘的数据通过加密存放在文本文件中 打开的文本文件为加密文本 需要通过软件查看已经存放
  • 用实际例子理解回调函数(Calback)

    用实际例子理解回调函数 Calback 在我们编码的过程中 调用和回调几乎无处不在 但是我对回调函数到底是怎样一回事并没有一个真正透彻的理解 最近我查找学习了一些资料 学到了很多 我参考了一些知乎上的分享 很不错 https www zhi