JAVA回调机制(CallBack)详解

1,714 阅读22分钟
原文链接: www.cnblogs.com
Bro__超

JAVA回调机制(CallBack)详解

序言

最近学习java,接触到了回调机制(CallBack)。初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义。当然了,我在理解了回调之后,再去看网上的各种讲解,确实没什么问题。但是,对于初学的我来说,缺了一个循序渐进的过程。此处,将我对回调机制的个人理解,按照由浅到深的顺序描述一下,如有不妥之处,望不吝赐教!

开始之前,先想象一个场景:幼稚园的小朋友刚刚学习了10以内的加法。

第1章. 故事的缘起

幼师在黑板上写一个式子 “1 + 1 = ”,由小明同学来填空。

由于已经学习了10以内的加法,小明同学可以完全靠自己来计算这个题目,模拟该过程的代码如下:

 1 public class Student
 2 {
 3     private String name = null;
 4 
 5     public Student(String name)
 6     {
 7         // TODO Auto-generated constructor stub
 8         this.name = name;
 9     }
10     
11     public void setName(String name)
12     {
13         this.name = name;
14     }
15     
16     private int calcADD(int a, int b)
17     {
18         return a + b;
19     }
20     
21     public void fillBlank(int a, int b)
22     {
23         int result = calcADD(a, b);
24         System.out.println(name + "心算:" + a + " + " + b + " = " + result);
25     }
26 }

小明同学在填空(fillBalnk)的时候,直接心算(clacADD)了一下,得出结果是2,并将结果写在空格里。测试代码如下:

 1 public class Test
 2 {
 3     public static void main(String[] args)
 4     {
 5         int a = 1;
 6         int b = 1;
 7         Student s = new Student("小明");
 8         s.fillBlank(a, b);
 9     }
10 }

 

运行结果如下:

小明心算:1 + 1 = 2

该过程完全由Student类的实例对象单独完成,并未涉及回调机制。

第2章. 幼师的找茬

课间,幼师突发奇想在黑板上写了“168 + 291 = ”让小明完成,然后回办公室了。

花擦!为什么所有老师都跟小明过不去啊?明明超纲了好不好!这时候小明同学明显不能再像上面那样靠心算来完成了,正在懵逼的时候,班上的小红同学递过来一个只能计算加法的计算器(奸商啊)!!!!而小明同学恰好知道怎么用计算器,于是通过计算器计算得到结果并完成了填空。

计算器的代码为:

1 public class Calculator
2 {
3     public int add(int a, int b)
4     {
5         return a + b;
6     }
7 }

修改Student类,添加使用计算器的方法:

 1 public class Student
 2 {
 3     private String name = null;
 4 
 5     public Student(String name)
 6     {
 7         // TODO Auto-generated constructor stub
 8         this.name = name;
 9     }
10     
11     public void setName(String name)
12     {
13         this.name = name;
14     }
15     
16     @SuppressWarnings("unused")
17     private int calcADD(int a, int b)
18     {
19         return a + b;
20     }
21     
22     private int useCalculator(int a, int b)
23     {
24         return new Calculator().add(a, b);
25     }
26     
27     public void fillBlank(int a, int b)
28     {
29         int result = useCalculator(a, b);
30         System.out.println(name + "使用计算器:" + a + " + " + b + " = " + result);
31     }
32 }

测试代码如下:

 1 public class Test
 2 {
 3     public static void main(String[] args)
 4     {
 5         int a = 168;
 6         int b = 291;
 7         Student s = new Student("小明");
 8         s.fillBlank(a, b);
 9     }
10 }

运行结果如下:

小明使用计算器:168 + 291 = 459

该过程中仍未涉及到回调机制,但是部分小明的部分工作已经实现了转移,由计算器来协助实现。

3. 幼师回来了

发现小明完成了3位数的加法,老师觉得小明很聪明,是个可塑之才。于是又在黑板上写下了“26549 + 16487 = ”,让小明上课之前完成填空,然后又回办公室了。

小明看着教室外面撒欢儿的小伙伴,不禁悲从中来。再不出去玩,这个课间就要废了啊!!!! 看着小红再一次递上来的计算器,小明心生一计:让小红代劳。

小明告诉小红题目是“26549 + 16487 = ”,然后指出填写结果的具体位置,然后就出去快乐的玩耍了。

这里,不把小红单独实现出来,而是把这个只能算加法的计算器和小红看成一个整体,一个会算结果还会填空的超级计算器。这个超级计算器需要传的参数是两个加数和要填空的位置,而这些内容需要小明提前告知,也就是小明要把自己的一部分方法暴漏给小红,最简单的方法就是把自己的引用和两个加数一块告诉小红。

因此,超级计算器的add方法应该包含两个操作数和小明自身的引用,代码如下:

1 public class SuperCalculator
2 {
3     public void add(int a, int b, Student  xiaoming)
4     {
5         int result = a + b;
6         xiaoming.fillBlank(a, b, result);
7     }
8 }

小明这边现在已经不需要心算,也不需要使用计算器了,因此只需要有一个方法可以向小红寻求帮助就行了,代码如下:

 1 public class Student
 2 {
 3     private String name = null;
 4 
 5     public Student(String name)
 6     {
 7         // TODO Auto-generated constructor stub
 8         this.name = name;
 9     }
10     
11     public void setName(String name)
12     {
13         this.name = name;
14     }
15     
16     public void callHelp (int a, int b)
17     {
18         new SuperCalculator().add(a, b, this);
19     }
20     
21     public void fillBlank(int a, int b, int result)
22     {
23         System.out.println(name + "求助小红计算:" + a + " + " + b + " = " + result);
24     }
25 }

测试代码如下:

 1 public class Test
 2 {
 3     public static void main(String[] args)
 4     {
 5         int a = 26549;
 6         int b = 16487;
 7         Student s = new Student("小明");
 8         s.callHelp(a, b);
 9     }
10 }

运行结果为:

小明求助小红计算:26549 + 16487 = 43036

执行流程为:小明通过自身的callHelp方法调用了小红(new SuperCalculator())的add方法,在调用的时候将自身的引用(this)当做参数一并传入,小红在使用计算器得出结果之后,回调了小明的fillBlank方法,将结果填在了黑板上的空格里。

灯灯灯!到这里,回调功能就正式登场了,小明的fillBlank方法就是我们常说的回调函数。

通过这种方式,可以很明显的看出,对于完成老师的填空题这个任务上,小明已经不需要等待到加法做完且结果填写在黑板上才能去跟小伙伴们撒欢了,填空这个工作由超级计算器小红来做了。回调的优势已经开始体现了。

第4章. 门口的婆婆

幼稚园的门口有一个头发花白的老婆婆,每天风雨无阻在那里摆着地摊卖一些快过期的垃圾食品。由于年纪大了,脑子有些糊涂,经常算不清楚自己挣了多少钱。有一天,她无意间听到了小明跟小伙伴们吹嘘自己如何在小红的帮助下与幼师斗智斗勇。于是,婆婆决定找到小红牌超级计算器来做自己的小帮手,并提供一包卫龙辣条作为报酬。小红经不住诱惑,答应了。

回看一下上一章的代码,我们发现小红牌超级计算器的add方法需要的参数是两个整型变量和一个Student对象,但是老婆婆她不是学生,是个小商贩啊,这里肯定要做修改。这种情况下,我们很自然的会想到继承和多态。如果让小明这个学生和老婆婆这个小商贩从一个父类进行继承,那么我们只需要给小红牌超级计算器传入一个父类的引用就可以啦。

不过,实际使用中,考虑到java的单继承,以及不希望把自身太多东西暴漏给别人,这里使用从接口继承的方式配合内部类来做。

换句话说,小红希望以后继续向班里的小朋友们提供计算服务,同时还能向老婆婆提供算账服务,甚至以后能够拓展其他人的业务,于是她向所有的顾客约定了一个办法,用于统一的处理,也就是自己需要的操作数和做完计算之后应该怎么做。这个统一的方法,小红做成了一个接口,提供给了大家,代码如下:

1 public interface doJob
2 {
3     public void fillBlank(int a, int b, int result);
4 }

因为灵感来自帮小明填空,因此小红保留了初心,把所有业务都当做填空(fillBlank)来做。

同时,小红修改了自己的计算器,使其可以同时处理不同的实现了doJob接口的人,代码如下:

1 public class SuperCalculator
2 {
3     public void add(int a, int b, doJob  customer)
4     {
5         int result = a + b;
6         customer.fillBlank(a, b, result);
7     }
8 }

小明和老婆婆拿到这个接口之后,只要实现了这个接口,就相当于按照统一的模式告诉小红得到结果之后的处理办法,按照之前说的使用内部类来做,代码如下:

小明的:

 1 public class Student
 2 {
 3     private String name = null;
 4 
 5     public Student(String name)
 6     {
 7         // TODO Auto-generated constructor stub
 8         this.name = name;
 9     }
10     
11     public void setName(String name)
12     {
13         this.name = name;
14     }
15     
16     public class doHomeWork implements doJob
17     {
18 
19         @Override
20         public void fillBlank(int a, int b, int result)
21         {
22             // TODO Auto-generated method stub
23             System.out.println(name + "求助小红计算:" + a + " + " + b + " = " + result);
24         }
25         
26     }
27     
28     public void callHelp (int a, int b)
29     {
30         new SuperCalculator().add(a, b, new doHomeWork());
31     }
32 }

老婆婆的:

 1 public class Seller
 2 {
 3     private String name = null;
 4 
 5     public Seller(String name)
 6     {
 7         // TODO Auto-generated constructor stub
 8         this.name = name;
 9     }
10     
11     public void setName(String name)
12     {
13         this.name = name;
14     }
15     
16     public class doHomeWork implements doJob
17     {
18 
19         @Override
20         public void fillBlank(int a, int b, int result)
21         {
22             // TODO Auto-generated method stub
23             System.out.println(name + "求助小红算账:" + a + " + " + b + " = " + result + "元");
24         }
25         
26     }
27     
28     public void callHelp (int a, int b)
29     {
30         new SuperCalculator().add(a, b, new doHomeWork());
31     }
32 }

测试程序如下:

 1 public class Test
 2 {
 3     public static void main(String[] args)
 4     {
 5         int a = 56;
 6         int b = 31;
 7         int c = 26497;
 8         int d = 11256;
 9         Student s1 = new Student("小明");
10         Seller s2 = new Seller("老婆婆");
11         
12         s1.callHelp(a, b);
13         s2.callHelp(c, d);
14     }
15 }

运行结果如下:

小明求助小红计算:56 + 31 = 87
老婆婆求助小红算账:26497 + 11256 = 37753元

最后的话

可以很明显的看到,小红已经把这件事情当做一个事业来做了,看她给接口命的名字doJob就知道了。

有人也许会问,为什么老婆婆摆摊能挣那么多钱? 你的关注点有问题好吗!!这里聊的是回调机制啊!!

我只知道,后来小红的业务不断扩大,终于在幼稚园毕业之前,用挣到的钱买了人生的第一套房子。

完!!!

 

posted on 2016-04-10 22:25 Bro__超 阅读(40620) 评论(46) 编辑 收藏

评论

#1楼 2016-04-11 00:12 幽明河  

易懂
支持(0)反对(0)   

#2楼 2016-04-11 08:36 落叶的博客  

楼主写的不错,学习了一下,哈哈,小弟也写了一个java 安卓回调的例子,相互学习一下。一个例子让你彻底理解java回调机制 支持(1)反对(0) http://pic.cnblogs.com/face/620620/20170309214403.png   

#3楼 2016-04-11 10:58 iPhone Dev  

还不错, 通俗易懂! 支持(0)反对(1) http://pic.cnblogs.com/face/u319302.png?id=04132745   

#4楼 2016-04-11 11:35 Kwok3  

终于在幼稚园毕业之前,用挣到的钱买了人生的第一套房子。 支持(0)反对(0) http://pic.cnblogs.com/face/694009/20170227190408.png   

#5楼 2016-04-12 09:06 旋剑  

看的我想写一个c++的回调函数 支持(1)反对(0)   

#6楼 2016-04-12 09:23 紫薇郎  

终于知道内部类的作用了 支持(0)反对(0)   

#7楼 2016-04-12 13:37 UserObject  

学习了 支持(0)反对(0) http://pic.cnblogs.com/face/826462/20151022224634.png   

#8楼 2016-04-12 13:45 玻璃鱼儿  

这不就是C#的委托么??不过这实现的有点让人费解 支持(0)反对(0) http://pic.cnblogs.com/face/u219720.jpg?id=19161728   

#9楼 2016-04-12 14:02 smallerpig  

@ 玻璃鱼儿
就因为java没有委托,所有用这种回调来做了 支持(0)反对(0) http://pic.cnblogs.com/face/357178/20160518101032.png   

#10楼 2016-04-12 14:18 SunnyCase  

@ 玻璃鱼儿
java没委托 只能通过类模拟 支持(0)反对(0) http://pic.cnblogs.com/face/638369/20150420213857.png   

#11楼 2016-04-12 14:32 者旨於陽  

写的很好 支持(0)反对(0) http://pic.cnblogs.com/face/712310/20160619164247.png   

#12楼 2016-04-12 22:21 cbwleft  

实际上就是策略模式 支持(1)反对(0)   

#13楼 2016-04-13 12:11 左潇龙  

其实就是传一段代码给某个方法A,然后方法A可以按照自己的需要在适当的时候执行这段传进来的代码。所有的回调应该都是这么个逻辑。 支持(0)反对(0) http://pic.cnblogs.com/face/558323/20130820130011.png   

#14楼 2016-04-14 09:31 houlei1992  

老师回来一看.小明本事不小.直接按他做了班长,换掉了小红.小红心里委屈,找小明理论.小明乘机表了白.15楼接. 支持(5)反对(0)   

#15楼 2016-04-15 01:17 gleox  

Java的闭包啊 支持(0)反对(0) http://pic.cnblogs.com/face/371678/20151216225118.png   

#16楼 2016-04-17 23:52 大熊先生|互联网后端技术  

139wx.com/ 支持(0)反对(0) http://pic.cnblogs.com/face/u119471.gif?id=04212452   

#17楼 2016-04-18 10:19 程序诗人  

bucuo 支持(0)反对(0) http://pic.cnblogs.com/face/47012/20131031113822.png   

#18楼 2016-04-19 23:26 lngg057  

例子举的理解不能,我感觉还是直接看解释来的快和清楚。 支持(0)反对(0) http://pic.cnblogs.com/face/496689/20150909201352.png   

#19楼 2016-04-20 23:31 cnzhengyuhao  

为什么要多做个内部类
比如说student直接实现dojob接口,callhelp传个this进去,这样不行吗 支持(2)反对(0)   

#20楼[楼主] 2016-04-21 09:14 Bro__超  

@ cnzhengyuhao
1.如果回调的时候 需要用到Student里面的private成员,那么就要把这些你不想暴露的属性公开出来。内部类的好处是:可以访问外部类的私有成员;
2.可以防止方法重名;
3.及其他 支持(3)反对(0) http://pic.cnblogs.com/face/649448/20170122193112.png   

#21楼 2016-04-22 10:42 微软一点都不软  

@ cnzhengyuhao
我也这么觉得,多此一举。 支持(0)反对(0)http://pic.cnblogs.com/face/u216616.jpg?id=10231011   

#22楼 2016-04-26 22:49 senzve  

楼主写的很有趣!! 支持(0)反对(0)   

#23楼 2016-05-06 17:19 小菜、day day up  

为什么老婆婆摆摊能挣那么多钱? 支持(1)反对(0)   

#24楼 2016-05-08 22:36 lulipro  

我喜欢这样风格的文章,实用!顶一个 支持(0)反对(0) http://pic.cnblogs.com/face/858860/20170316220126.png   

#25楼 2016-05-18 10:21 困兽斗  

我觉得用内部类恰恰是一种有趣的方式,当然要根据具体业务决定内部类是否必要。
假如我们不希望小明和老婆婆把fillBlank的功能直接暴漏给用户代码调用,那小明和老婆婆如果直接去实现doJob接口,势必要直接暴漏。那么用户代码不callHelp,而是直接调用fillBlank会怎样?
假如上述情况正是你不希望看到的,那么我觉得用内部类这种方式,挺合适。而且小明的接口实现跟别人不同,那么他的内部类doHomeWork就可以有权不被别人“看到”。 支持(3) 反对(0)   

#26楼 2016-05-26 09:42 StephenSen  

学习了。有一点,public class doHomeWork implements doJob......
类名和接口名首字母最好要大写。 支持(1)反对(0)   

#27楼 2016-05-30 14:41 tancp  

学习了,java回调机制及内部类的使用 支持(0)反对(0)http://pic.cnblogs.com/face/611087/20170207142223.png   

#28楼 2016-06-13 16:55 乐雨  

写的通俗易懂 支持(0)反对(0)http://pic.cnblogs.com/face/u337684.jpg?id=03211239   

#29楼 2016-11-01 00:02 疾风追梦  

很赞!完全理解了java内部类 支持(0)反对(0)   

#30楼 2016-11-05 10:49 小不了  

请问博主大大,文章可以转载吗? 支持(0)反对(0)   

#31楼[楼主] 2016-11-05 11:57 Bro__超  

@ 小不了
当然可以,不过这篇博客并没涉及异步回调,还是缺了一些精髓的。 支持(0)反对(0) http://pic.cnblogs.com/face/649448/20170122193112.png   

#32楼 2016-11-05 14:40 小不了  

@ Bro__超
已经非常棒了!楼主还会更新不? 支持(0)反对(0)   

#33楼 2016-11-08 16:44 小不了  

mp.weixin.qq.com/s?__biz=MzA4MjYyOTQ0Mg==&mid=2649688585&idx=1&sn=7ed216d2665f8998c0f64ebc544682fe&chksm=87996c24b0eee53290f62561199f627a3c03bd0b0a16be2f73fcb59de78adc0a7dcbfc31955e#rd

谢谢博主大大的优质内容~ 支持(0)反对(0)   

#34楼 2017-01-04 16:16 unicornoo  

哈哈哈 支持(0)反对(0)   

#35楼 2017-01-04 16:16 unicornoo  

果断关注 支持(0)反对(0)   

#36楼 2017-03-23 15:49 czxing99  

因为我看jdbcTemplate里query的回调才找到这篇文章,我试着把例子改了 。成功是成功了,但不好理解。(自学的JAVA,没用JAVA上过班,书面语言我自己解释不通。有大神完善例子没。
public interface RowCallbackHandlerTest {
public void fillBlank( int result);
}

public class SuperCalculator {
public void add(int c, RowCallbackHandlerTest customer)
{
customer.fillBlank(c);
}
}
//自己构建回调方法Student类里
public void callHelpChange(int a ,int b,RowCallbackHandlerTest rowCallbackHandlerTest ){
int c = a + b;
//类似于从SQL得到 ResultSet
new SuperCalculator().add(c, rowCallbackHandlerTest);
}
//增l回Cmp类
//test(类)
public static void main(String[] args) {
Student s1 = new Student("小明");
int a = 20;
int b = 120;
final Cmp cmp = new Cmp();
s1.callHelp(a, b);
s1.callHelpChange(a, b, new RowCallbackHandlerTest() {
@Override
public void fillBlank( int result) {
// TODO Auto-generated method stub
cmp.setValue(result );
System.out.println("cllHelpChange回调方法:result = "+ result );
System.out.println("cllHelpChange回调方法:cmp.getValues() = "+ cmp.getValue() );
}
});
想了想还是接口作为参数和匿名内部类概念不熟悉,基础太差了。 支持(0)反对(0)   

#37楼 2017-04-07 11:20 甘乐  

弱弱地问一下如果我直接用student类和Seller类实现doJob接口会怎么样? 支持(0)反对(0)http://pic.cnblogs.com/face/836208/20160115214349.png   

#38楼 2017-07-15 11:05 nc明了  

@ 甘乐
Student的老师回来了,发现了小明“天才”的真相,拨通了小明家长的电话...;
Seller表示很无所谓,有没有人管我,做个小生意而已,用计算器不犯法; 支持(0)反对(0)   

#39楼 2017-07-22 15:32 NYS  

栋梁之才,好好活着,总有一天会被重用 支持(0)反对(0)   

#40楼 2017-07-23 16:58 NYS  

我知道你们背后势力庞大,你们用计对我进行诬陷,再操纵舆论,让很多人相信你们。
我家人朋友甚至不相干的人都因我受到你们背后势力的伤害和威胁,我愿意屈从于你们的淫威,因为我不愿意看到更多人受伤害。 支持(0)反对(0)   

#41楼 2017-08-02 11:30 落魄亡魂  

可以可以 毫无违和感。666 支持(0)反对(0)   

#42楼 2017-08-22 10:07 blue_wz  

public class SuperCalculator
{
public void add(int a, int b, Student xiaoming)
{
int result = a + b;
xiaoming.fillBlank(a, b, result);
}
}
楼主,这里我请教一下,小明调用小红的add方法,目的就是得到两个数相加的值。那么这里为何小红的add方法不改为:
public void add(int a, int b, Student xiaoming)
{
int result = a + b;
return result;
}
}
这样的话就省去了回调的环节。
希望楼主能在这个例子上加深讲解一下,谢谢! 支持(0)反对(0)http://pic.cnblogs.com/face/1024359/20160910185242.png   

#43楼 2017-11-07 16:54 _今晚打老虎  

@ blue_wz
回调大部分是和异步一起用的,好比小明将数字报给小红后就可以出去玩了,等玩好了回来就可以拿到小红算好的结果,然后再写到黑板上(fillBlank:).如果用返回值就是同步操作了(返回值类型是int不是void哦). 支持(1) 反对(0)   

#44楼 2017-11-07 17:05 blue_wz  

@ _今晚打老虎
感谢楼主的解答,我想我明白了。不过这时间也太久了点 = =.. 支持(0)反对(0)http://pic.cnblogs.com/face/1024359/20160910185242.png   

#45楼 2018-03-09 16:56 猴子童鞋  

深入浅出,牛人标配 支持(0)反对(0)http://pic.cnblogs.com/face/1116296/20170327010116.png   

#46楼39392102018/4/1 11:20:49 2018-04-01 11:20 Lion_  

非常不错,简单易懂,一下子就明白了,赞楼主 支持(0)反对(0)http://pic.cnblogs.com/face/561245/20130903124353.png    刷新评论刷新页面返回顶部 注册用户登录后才能发表评论,请 登录注册访问网站首页。 【推荐】超50万VC++源码: 大型组态工控、电力仿真CAD与GIS源码库!
【报名】2050 大会 - 博客园程序员聚会(5.25 杭州·云栖小镇)
【招聘】花大价钱找技术大牛我们是认真的!
【腾讯云】买域名送解析+SSL证书+建站
qcloud_C1_0402 最新IT新闻:
· 静态码支付限额500元/天,微信支付宝超额怎么办
· 今日头条:将全面清查内容 暂停新注册用户传视频
· 乐视网称对酷派投资计提减值约4.68亿元
· 王冠雄:美团收购摩拜的七个幕后关键问题
· Dropbox继续从IPO承销银行处筹集1.13亿美元
» 更多新闻... 阿里云0308 最新知识库文章:
· 写给自学者的入门指南
· 和程序员谈恋爱
· 学会学习
· 优秀技术人的管理陷阱
· 作为一个程序员,数学对你到底有多重要
» 更多知识库文章...
< 2018年4月 >
25 26 27 28 29 30 31
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 1 2 3 4 5

导航

统计

  • 随笔 - 5
  • 文章 - 0
  • 评论 - 102
  • 引用 - 0

公告

昵称:Bro__超
园龄:3年8个月
粉丝:33
关注: 0 +加关注

搜索

   

常用链接

我的标签

随笔分类

随笔档案

最新评论

阅读排行榜

评论排行榜

推荐排行榜