对于技术问题,会用是一回事,理解这个技术问题的来龙去脉、设计者当初为什么要设计这个功能、这个技术问题有哪些优势、适用哪些场景又是另外回事了。

前者照猫画虎得其形,后者形神兼备得其意,这也是所谓青铜与王者的区别。

会使用、又能理解已经不易,如果还能表达出来,讲所有人都能看明白就更不容易了。

在软件开发这个行业,回调函数是最常见的功能。在图形界面开发的时候,每一个按钮的点击事件都会用到回调函数。

  • 什么是回调函数?

  • 为什么要用回调函数?

  • 在java中,为什么要用接口来实现回调函数?

这是要弄明白的几个问题!

什么是函数调用?

在弄清楚什么是回调函数之前,先要明白什么函数调用。“调用”的英语叫“call”,翻译过来也叫“打电话”。

打电话无非是沟通、协调、安排一些事情,或者还要得到自己想要的结果。

编程就是对现实的模拟和抽象,一定要理解这句话,下面我们就模拟一下。

我打电话(call)给老婆,问晚上要买点什么菜回家,老婆说买点五花肉回来晚上做回锅肉。我得到了想要的结果,这说明我调用这个函数得到了想要的返回值。

模拟代码:

public String callWife(String 什么菜){//一顿沟通,自行脑补return "五花肉";
}

我给孩子打电话,叮嘱他要完成家庭作业之后才能看电视。这次函数调用没有返回值,就像在函数体里面写了一句"我想你"一样。

模拟代码:

public void callBaby(){System.out.println("I miss you");
}

这些都很好理解,可如果一个电话不能达到自己的目的呢?

我给朋友打电话,问什么时候能约一下其它几个朋友一起吃个饭,他说要先给他们沟通一下什么时候有时间,沟通好了再通知我,具体需要沟通多久也不确定。

这个问题就不是一个电话(一次函数调用)就能解决问题了,需要再打电话回来(callback)才能达到自己的目的。

但此处要记住几个关键点:

  1. 打电话给朋友,相当于我调用了函数callFriend()
  2. 接到朋友的通知,相当于朋友调用了我的一个函数。
  3. 我是这次交互的主体。

什么是回调函数?

在英语中,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的回调函数?相关推荐

  1. Java基础:回调函数

    因为在看Android代码的时候发现了许多关于回调函数的知识, 所以去了解了一下. 对于我来说不太好懂, 因为我觉得看的那些博文的讲法对我来说很绕, 所以我在理解了之后想写一篇关于回调函数的博文来给和 ...

  2. 彻底理解JavaScript中回调函数 (推荐)

    在javascript中回调函数非常重要,它们几乎无处不在.像其他更加传统的编程语言都有回调函数概念,但是非常奇怪的是,完完整整谈论回调函数的在线教程比较少,倒是有一堆关于call()和apply() ...

  3. java socket 回调函数_请问Java网络编程如何在不使用多线程的情况下实现异步返回?...

    我指的是在不使用多线程的情况下进行并发处理 具体的情况是,在不使用多线程的情况下,服务器侦听某个端口,在有连接进来的时候会调用某个函数对此连接进行处理,但是由于处理的过程可能会比较长,为了不让后面连接 ...

  4. java反射回调函数_用J2V8注册Java回调函数

    J2V8是一套针对谷歌的V8 JavaScript引擎的Java绑定.J2V8的开发为Android平台带来了高效的Javascript的执行环境,taris.js 就是基于J2V8开发的.J2V8同 ...

  5. java jna 回调函数_JNA中级篇 回调函数详解

    JNI 技术是双向的,既可以从Java 代码中调用原生函数,也可以从原生函数中直接创建 Java 虚拟机,并调用Java 代码.但是在原生函数中调用java代码要写大量C代码,这对大多数java程序员 ...

  6. Java设计模式-回调函数和观察者模式

    Android的框架中有非常多的地方使用了回调的模式,例如Activity的生命周期,按钮的点击事件等. 下面是回调的基本模型: public class A {private CallBack ca ...

  7. Objective-C利用协议实现回调函数(类似java的回调函数)

    实现的代码如下: 定义协议: #import <UIKit/UIKit.h> @protocol NoteDelegate //回调函数 -(void)messageCallBack:(N ...

  8. java 的回调函数

    在Java中,通常就是编写另外一个类或类库的人(李四)规定一个接口,然后你(张三)来实现这个接口,然后把这个实现类的一个对象作为参数传给别人的程序,别人的程序必要时就会通过那个接口来调用你编写的函数 ...

  9. java反射回调函数_java回调函数

    最近在学习Struts2,了解到过滤器和拦截器的不同,过滤器底层原理是基于回调方法,而拦截器是基于反射,然后递归 感觉有个通俗的解释: 用客户端和服务端作比喻,比如客户端A和服务端B, A向B发消息, ...

最新文章

  1. 机器学习实战源码数据集
  2. SpringBoot------全局异常捕获和自定义异常
  3. 从零开始开发JVM语言(十一)Lambda
  4. vue radio双向绑定_Vue是如何实现双向数据绑定的
  5. CnGAN:面向跨网用户偏好推荐的生成对抗网络
  6. 2019ICPC(银川) - Delivery Route(强连通缩点+分块最短路)
  7. C#程序开机启动与获取程序启动路径
  8. 机器学习算法中的准确率、精确率、召回率和F值
  9. 《Oracle comment on注释信息方法论》
  10. python命名空间特性_PHP关键特性之命名空间实例
  11. VS2013配置Qt5.8.0环境
  12. Android 百度地图开发问题----解决地图有时候加载不出来问题
  13. win32开发(画笔、刷子、字体)
  14. php 检测 变量是否设置,php判断变量是否定义
  15. 注册表的使用-入门篇
  16. Netstat命令使用方法
  17. Windows Error Code
  18. python离线安装第三方库whl_详细说明如何在pycharm不联网的情况下,离线安装第三方库及依赖包(如sklearn)...
  19. Address localhost:1099 is already in use
  20. 西门子博途系列学习笔记SCL(一)

热门文章

  1. 「营业日志 2020.12.10」Jiangly 的排列数数题
  2. 有没有什么靠谱的副业?
  3. yum install时提示This system is not registered with an entitlement server
  4. 一个http请求的全过程是怎样的?
  5. Bzoj1502【NOI2005】月下柠檬树
  6. 笔试 面试题 网友汇总(放在自己的文章列表里)
  7. WordPress资讯小程序源码分享
  8. 四年级计算机教案模板,小学四年级计算机教案模板.doc
  9. VSCode鼠标滚轮控制字体大小
  10. C++笔试题大全----下