如何理解java的回调函数?
对于技术问题,会用是一回事,理解这个技术问题的来龙去脉、设计者当初为什么要设计这个功能、这个技术问题有哪些优势、适用哪些场景又是另外回事了。
前者照猫画虎得其形,后者形神兼备得其意,这也是所谓青铜与王者的区别。
会使用、又能理解已经不易,如果还能表达出来,讲所有人都能看明白就更不容易了。
在软件开发这个行业,回调函数是最常见的功能。在图形界面开发的时候,每一个按钮的点击事件都会用到回调函数。
什么是回调函数?
为什么要用回调函数?
在java中,为什么要用接口来实现回调函数?
这是要弄明白的几个问题!
什么是函数调用?
在弄清楚什么是回调函数之前,先要明白什么函数调用。“调用”的英语叫“call”,翻译过来也叫“打电话”。
打电话无非是沟通、协调、安排一些事情,或者还要得到自己想要的结果。
编程就是对现实的模拟和抽象,一定要理解这句话,下面我们就模拟一下。
我打电话(call)给老婆,问晚上要买点什么菜回家,老婆说买点五花肉回来晚上做回锅肉。我得到了想要的结果,这说明我调用这个函数得到了想要的返回值。
模拟代码:
public String callWife(String 什么菜){//一顿沟通,自行脑补return "五花肉";
}
我给孩子打电话,叮嘱他要完成家庭作业之后才能看电视。这次函数调用没有返回值,就像在函数体里面写了一句"我想你"一样。
模拟代码:
public void callBaby(){System.out.println("I miss you");
}
这些都很好理解,可如果一个电话不能达到自己的目的呢?
我给朋友打电话,问什么时候能约一下其它几个朋友一起吃个饭,他说要先给他们沟通一下什么时候有时间,沟通好了再通知我,具体需要沟通多久也不确定。
这个问题就不是一个电话(一次函数调用)就能解决问题了,需要再打电话回来(callback)才能达到自己的目的。
但此处要记住几个关键点:
- 我打电话给朋友,相当于我调用了函数callFriend()
- 我接到朋友的通知,相当于朋友调用了我的一个函数。
- 我是这次交互的主体。
什么是回调函数?
在英语中,callback是回电话的意思,而在开发中callback叫回调函数,其实就是回电话的意思,callback这一个单词已经把“回调”的神表达出来了,作为开发者,完全可以按回电话的场景来理解回调函数。
仔细咂摸一下,回电话这个场景与回调函数的使用场景何其像啊!点击一个按钮或链接,然后等待数据的返回和界面的刷新,具体等待多久也不确定,可能0.1秒就响应了,也可能5秒之后才响应,总之,响应时间是不确定的,不能让人一直等着,这不就像等一个回电话的过程吗?
下面我用实际代码模拟上面的沟通过程
这个过程涉及到我和朋友两个实体类,Me类代表我,Friend类代表我的朋友。
因为我要打电话给朋友和接朋友电话通知,所以Me类有三个功能,也叫行为:给朋友打电话,我们用callFriend(Friend)来表示;接到朋友的通知,我们用noticeMe()函数来表示;在给朋友打电话和接朋友电话通知之间的这一段时间,我在忙其它的事情,我们用doOtherthing()函数来表示。
朋友要先给其他小伙伴约时间然后给我打电话,所以Friend类也有个函数order()定义他和其它小伙伴的预约过程,约好时间之后又要给我回电话,所以还要有个参数为Me的对象。
代码如下,这些代码是可以直接运行的,并且用数字对程序的运行顺序做了标记。
Me.java的代码
public class Me {// 给朋友打电话public void callFriend(Friend friend) throws InterruptedException {System.out.println("1、我打电话给朋友,让他去约时间");friend.order(this);doOtherThing();}// 通知我public void noticeMe() {System.out.println("6、我收到了朋友的通知");}// 忙其它的事情去了public void doOtherThing() {System.out.println("3、我去忙其它的事情...");}
}
Friend.java
public class Friend {//和其他小伙伴约好时间后通知我public void order(final Me me) throws InterruptedException {System.out.println("2、朋友接到电话说:我现在就和其他小伙伴约时间,请稍等...");new Thread(new Runnable() {public void run() {try {System.out.println("4、朋友正在电话联系中....");sleep(5000);System.out.println("5、朋友已经约好了,准备给我回电话");me.noticeMe();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}
对这段代码加以说明:
- 函数中使用线程是为了模拟朋友的沟通过程,在实际应用中多为耗时的操作。
- “朋友”沟通过程与“我”忙其它事情是并行的,“我”并没有等待他。
最后,写主程序
//测试回调的主类
public class Main {public static void main(String[] args) throws InterruptedException {// 创建我和朋友的对象Me me = new Me();Friend friend = new Friend();
// 给朋友打电话me.callFriend(friend);}
}
看运行结果
从结果中可以看出,我联系朋友之后就忙其它事情了,朋友接到电话之后就去沟通,我也不知道他会沟通多久,朋友在沟通完之后通知我,就完事了。
从主程序可以看出,只有我调用了callFriend(),朋友怎么沟通、怎么回电话,我都不关注,他只需要持有“我”对象的引用就行了,需要通知我时,就通过引用调用noticeMe()。
有主动调用过程,也有callBack的过程,这个实例已经展示了回调的全部了,但显然并不完美
试想以下场景:
- 老板安排给员工一个任务,要求完成工作后通知老板
- 父母安排你飞机落地后,给他们报个平安。
生活中这样的场景非常多,在实际项目中也是,但是在项目中,我们在每个类中定义一个类似noticeMe()这样的类就不高明了,因为违背了“复用”的编程原则,虽然实现了回调的功能,但并不是真正意义上的回调函数。
所以我们可以对“通知”这一行为进一步抽象,对行为抽象当然要使用接口。
具体代码如下:
/*定义接收通知接口,所有参与者可实现此接口以接收通知*/
public interface CallBack {//参与者可以实现接收通知的细节public void receiveNotice(String friendName);
}
这里有个参数friendName,朋友通知我的时候,可以通过这个参数传递一些信息,比如他的名字。
Me.java实现这个接口后的代码
public class Me implements CallBack{// 给朋友打电话public void callFriend(Friend friend) throws InterruptedException {System.out.println("1、我打电话给朋友,让他去约时间");friend.order(this);doOtherThing();}// public void noticeMe() {//
// }// 忙其它的事情去了public void doOtherThing() {System.out.println("3、我去忙其它的事情...");}// 通知我public void receiveNotice(String friendName) {System.out.println("6、我收到了朋友 -"+friendName+ "- 的通知");}
}
Friend.java做相应的修改后:
public class Friend {//和其他小伙伴约好时间后通知我public void order(final CallBack callBack) {System.out.println("2、朋友接到电话说:我现在就和其他小伙伴约时间,请稍等...");new Thread(new Runnable() {public void run() {try {System.out.println("4、朋友正在电话联系中....");sleep(5000);System.out.println("5、朋友已经约好了,准备给我回电话");callBack.receiveNotice("张三");} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}
Main.java的代码不需要调整。
运行结果和前面基本是一致的,我只是增加了一个参数,大家也可以体会一个这个参数带来的便利。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JWz7ZZvj-1607071761440)(D:\markdown\前端学习笔记.assets\image-20201204155651918.png)]
多了一个接口的定义,代码好像更多了。但在实际扩展当中却更方便了,比如说,老板要接收员工的通知,老板类实现这个接口就完事了;父母要等待孩子的通知,父母类实现这个接口就可以。换句话说,所有要扩展接收通知的类,只要实现这个接口就具备了接收通知的功能。
这样的好处是代码得到了复用,极大的方便了扩展。
通过接口来实现回调的功能,这才是真正意义上的回调函数。
android中的控件点击,都是用回调函数来实现的,比如Button,View.OnClickListener就是接口,只有一个onClick()函数,类似上面的receiveNotice()函数,只是此处用匿名内部类实现了这个接口。java虚拟机监听着按钮,只要按钮被点击,onClick()就会被调用,而上面的例子是我们在代码中调用的,实质并不区别。
button.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {//点击后的具体实现}});
这就是我对回调函数的理解,也欢迎与大家就此问题进行交流。
如何理解java的回调函数?相关推荐
- Java基础:回调函数
因为在看Android代码的时候发现了许多关于回调函数的知识, 所以去了解了一下. 对于我来说不太好懂, 因为我觉得看的那些博文的讲法对我来说很绕, 所以我在理解了之后想写一篇关于回调函数的博文来给和 ...
- 彻底理解JavaScript中回调函数 (推荐)
在javascript中回调函数非常重要,它们几乎无处不在.像其他更加传统的编程语言都有回调函数概念,但是非常奇怪的是,完完整整谈论回调函数的在线教程比较少,倒是有一堆关于call()和apply() ...
- java socket 回调函数_请问Java网络编程如何在不使用多线程的情况下实现异步返回?...
我指的是在不使用多线程的情况下进行并发处理 具体的情况是,在不使用多线程的情况下,服务器侦听某个端口,在有连接进来的时候会调用某个函数对此连接进行处理,但是由于处理的过程可能会比较长,为了不让后面连接 ...
- java反射回调函数_用J2V8注册Java回调函数
J2V8是一套针对谷歌的V8 JavaScript引擎的Java绑定.J2V8的开发为Android平台带来了高效的Javascript的执行环境,taris.js 就是基于J2V8开发的.J2V8同 ...
- java jna 回调函数_JNA中级篇 回调函数详解
JNI 技术是双向的,既可以从Java 代码中调用原生函数,也可以从原生函数中直接创建 Java 虚拟机,并调用Java 代码.但是在原生函数中调用java代码要写大量C代码,这对大多数java程序员 ...
- Java设计模式-回调函数和观察者模式
Android的框架中有非常多的地方使用了回调的模式,例如Activity的生命周期,按钮的点击事件等. 下面是回调的基本模型: public class A {private CallBack ca ...
- Objective-C利用协议实现回调函数(类似java的回调函数)
实现的代码如下: 定义协议: #import <UIKit/UIKit.h> @protocol NoteDelegate //回调函数 -(void)messageCallBack:(N ...
- java 的回调函数
在Java中,通常就是编写另外一个类或类库的人(李四)规定一个接口,然后你(张三)来实现这个接口,然后把这个实现类的一个对象作为参数传给别人的程序,别人的程序必要时就会通过那个接口来调用你编写的函数 ...
- java反射回调函数_java回调函数
最近在学习Struts2,了解到过滤器和拦截器的不同,过滤器底层原理是基于回调方法,而拦截器是基于反射,然后递归 感觉有个通俗的解释: 用客户端和服务端作比喻,比如客户端A和服务端B, A向B发消息, ...
最新文章
- 机器学习实战源码数据集
- SpringBoot------全局异常捕获和自定义异常
- 从零开始开发JVM语言(十一)Lambda
- vue radio双向绑定_Vue是如何实现双向数据绑定的
- CnGAN:面向跨网用户偏好推荐的生成对抗网络
- 2019ICPC(银川) - Delivery Route(强连通缩点+分块最短路)
- C#程序开机启动与获取程序启动路径
- 机器学习算法中的准确率、精确率、召回率和F值
- 《Oracle comment on注释信息方法论》
- python命名空间特性_PHP关键特性之命名空间实例
- VS2013配置Qt5.8.0环境
- Android 百度地图开发问题----解决地图有时候加载不出来问题
- win32开发(画笔、刷子、字体)
- php 检测 变量是否设置,php判断变量是否定义
- 注册表的使用-入门篇
- Netstat命令使用方法
- Windows Error Code
- python离线安装第三方库whl_详细说明如何在pycharm不联网的情况下,离线安装第三方库及依赖包(如sklearn)...
- Address localhost:1099 is already in use
- 西门子博途系列学习笔记SCL(一)