为什么要使用内部类?内部类继承自某个类或者实现某个接口,内部类的代码可以操作外嵌类的对象. 这不是使用内部类的理由. 那么为什么使用内部类呢? 我觉得如果使用其他办法可以更好的解决需求问题,那为什么要使用那么复杂的内部类呢?

内部类的好处之一,可以提供更强的封装性. 像前面一篇中的实例,很多时候,我们甚至都不需要知道内部类的具体类型就可以使用它了. 但是这个理由说服力度不够,更重要的是,内部类提供了一种更合理的多重继承的解决方案. 因为每个内部类都可以独立的继承一个实现.

埃大爷说,除了解决多重继承的问题之外,内部类还有一些优良的特征:

  • 内部类可以有多个实例,每个实例都有自己的状态信息. 并且与外嵌类对象的信息相互独立.
  • 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口.
  • 内部类是一个独立的实体,不会存在”is-a”的关系
  • 内部类的对象创建的时刻 并不依赖于外嵌类创建的时刻(The point of creation of the inner-class object is not tied to the creation of the outer-class object. 这个是啥意思?)

闭包与回调

真是智商捉急,花了两天时间才略微搞明白什么是闭包什么是回调. 很神奇的是,搜了很多文档,最后竟然是在知乎上看到一个别人的回答之后豁然开朗. 发现知乎真是个神奇的地方…本节的很多内容借鉴了知乎用户futeng的回答,原文地址:https://www.zhihu.com/question/19801131/answer/26586203

闭包是一个可调用的对象,它包含了创建它的作用域的信息. 按照埃大爷的说法,闭包其实就是一个由函数和其引用环境组合成的一个实体. 先看埃大爷的代码:

1.interface Incrementable{
2. void increment();
3.}
4.
5.class Callee1 implements Incrementable{
6.
7. private int i = 0;
8. public void increment(){
9. i++;
10. System.out.println(i);
11. }
12.}
13.
14.class MyIncrement{
15. public void increment() {
16. System.out.println("Other Operation");
17. }
18. static void f(MyIncrement mi){
19. mi.increment();
20. }
21.}
22.
23.class Callee2 extends MyIncrement{
24.
25. private int i =0;
26. public void increment() {
27. super.increment();
28. i++;
29. System.out.println(i);
30. }
31.
32. private class Closure implements Incrementable{
33. public void increment() {
34. Callee2.this.increment();
35. System.out.println(Callee2.this);
36. }
37. }
38. Incrementable getCallBackReference(){
39. return new Closure();
40. }
41.}
42.
43.class Caller{
44. private Incrementable callbackReference;
45. public Caller(Incrementable cbh) {
46. callbackReference = cbh;
47. }
48. void go(){
49. callbackReference.increment();
50. }
51.}
52.
53.
54.public class Callbacks {
55. public static void main(String[] args) {
56. Callee1 c1 = new Callee1();
57. Callee2 c2 = new Callee2();
58. System.out.println(c2);
59. MyIncrement.f(c2);
60.
61. Caller caller1 = new Caller(c1);
62. Caller caller2 = new Caller(c2.getCallBackReference());
63.
64. caller1.go();
65. caller1.go();
66. caller2.go();
67. caller2.go();
68. }
69.}

32-37行这样一个内部类其实就是一个闭包,它知道所创建它的作用域的信息. 简单来说,就是Closure这个类的实例手里有它外嵌类的引用. 所以它知道它的作用域,也就是这个外嵌类实例的信息. 我的个人理解,不知道对不对.

关于回调
当一个方法调用另外一个实例的方法的时候,被调用的这个方法的执行依赖于调用者的某个方法. 被调用的方法会去调用调用者的某个方法. 被调用方法调用的调用者的这个方法就是回调方法. (跟绕口令似的)


图片来自维基百科.

比如酒店的叫醒服务. 酒店(Hotel)提供一个叫醒服务方法叫wakeUp(). 但是它允许你自己定义被叫醒的方式beWaked().你可以在beWaked()中定义自己被叫醒的方式,是敲门,打电话还是要求服务员踹开门把你从床上拎起来. 使用的时候,把beWaked()方法传入wakeUp(). 到了时间,酒店就可以调用你的beWaked方法来把你叫醒.

1.public class Guest {
2.
3. public void beWaked() {
4. System.out.println("call me via phone");
5. }
6. public static void main(String[] args) {
7. Guest guest = new Guest();
8. Hotel hotel = new Hotel();
9.
10. hotel.wake(guest);
11. }
12.}
13.
14.public class Hotel {
15. public void wake(Guest guest){
16. guest.beWaked();
17. }
18.}

这是一个比较简单的例子.

感觉知乎答友futeng的例子更好. 直接上终极版:

1.public interface DoHomework {
2. void doHomeWork(String question,String answer);
3.}
4.//========================================================================
5.public class Student implements DoHomework{
6. public void doHomeWork(String homework,String answer) {
7. System.out.println("作业本");
8. if("1+1=?".equals(homework)){
9. System.out.println("作业: "+homework+"答案: "+answer);
10. }else {
11. System.out.println("作业: "+homework+"答案: 不知道~");
12. }
13. }
14.
15. public static void main(String[] args) {
16. Student student = new Student();
17. String aHomework = "1+1=?";
18. RoomMate roomMate = new RoomMate();
19. roomMate.getAnswer(aHomework, student);
20.
21. }
22.}
23.//========================================================================
24.public class RoomMate {
25.
26. public void getAnswer(String homework, DoHomework someone) {
27. if("1+1=?".equals(homework)) {
28. someone.doHomeWork(homework, "2333333");
29. } else {
30. someone.doHomeWork(homework, "(空白)");
31. }
32. }
33.
34.}

看这个例子,其实跟上面的意思差不离. 学霸好室友提供代写作业服务getAnswer(). 只需要将作业题目和自己的引用传递给他,他就会帮你写作业. 但是这里需要注意的是,实际传入的是一个接口. 这里真是豁然开朗,之前对向上转型一直是心存疑虑的,这里提供了一个很好的使用向上转型的场景. 比如这里其实是可以直接传入student实例的引用,但是这样很不好. 我只是想让你帮我写作业. 但是我把整个引用都给你了,等于把自己所有接口都暴露出去了. 那样对student而言岂不是很不安全?
所以这里可以让student实现一个DoHomework的function接口,作为有代写作业职业操守的学霸好室友,我只要求”传入”这个接口. 这样学霸好室友就只能看到doHomeWork这一个方法. 同时也提供了更强的扩展. 那其他实现了DoHomework接口的人也可以找我写作业了.

1.public class RoomMate{
2. public void getAnswer(String homework,DoHomework someone) {
3. if("1+1=?".equals(homework)) {
4. someone.doHomeWork(homework, "2333333");
5. } else {
6. someone.doHomeWork(homework, "(空白)");
7. }
8. }
9. public static void main(String[] args) {
10. RoomMate roomMate = new RoomMate();
11. roomMate.getAnswer("1+1=?", new DoHomework() {
12.
13. @Override
14. public void doHomeWork(String question, String answer) {
15. System.out.println("问题"+question+" 答案: "+answer);
16. }
17. });
18. }
19.}

还有这个匿名内部类,这也是一种形式的回调. 理解起来不难,回调的时候,只要找到主调函数所需要的那个函数就可以了. 具体它是被定义在student类里还是一个匿名内部类里不重要. 画了一个聊胜于无的图…我觉得我貌似看懂了…

再回头看埃大爷的代码,Caller类中定义的go()方法其实就用到了回调.

TJI读书笔记14-闭包与回调的更多相关文章

  1. TJI读书笔记17-字符串

    TJI读书笔记17-字符串 不可变的String 重载”+”和StringBuilder toString()方法的一个坑 String上的操作 格式化输出 Formatter类 字符串操作可能是计算 ...

  2. TJI读书笔记16-异常处理

    TJI读书笔记16-异常处理 概念 基本异常情形 异常的捕获 自定义异常 异常说明 捕获所有异常 栈轨迹 重新抛出异常 Java标准异常 使用finally 异常的限制 构造器 异常的匹配 其他乱七八 ...

  3. TJI读书笔记15-持有对象

    TJI读书笔记15-持有对象 总览 类型安全和泛型 Collection接口 添加元素 List 迭代器 LinkedList 栈 Set Map Queue Collection和Iterator ...

  4. TJI读书笔记13-内部类

    TJI读书笔记13-内部类 TJI读书笔记13-内部类 创建内部类 内部类和外部类的关系 .this和.new 内部类和向上转型 局部内部类 匿名内部类 匿名内部类的定义和初始化 使用匿名内部类来实现 ...

  5. TJI读书笔记12-接口

    TJI读书笔记12-接口 抽象类和抽象方法 接口 完全解耦和策略模式 接口间的继承关系 工厂模式 乱七八糟不知道怎么归类的知识点 接口和抽象类为我们提供了更强又有力的接口和实现分离的方法. 抽象类和抽 ...

  6. TJI读书笔记10-复用类

    TJI读书笔记10-复用类 组合语法 继承语法 代理 final关键字 final的数据 final的参数 final的方法 final的类 初始化和类的加载 乱七八糟不知道怎么归类的知识点 代码复用 ...

  7. TJI读书笔记11-多态

    TJI读书笔记11-多态 再说说向上转型 多态的原理 构造器和多态 协变返回类型 使用继承进行设计 多态是数据抽象和继承之后的第三种基本特征. 一句话说,多态分离了做什么和怎么做(再次对埃大爷佩服的五 ...

  8. TJI读书笔记09-访问控制权限

    TJI读书笔记09-访问控制权限 包,package和import 权限修饰符 接口和实现 类的访问权限控制 首先问一个问题,为什么要有访问控制权限? 安全,这当然是一个很重要的原因. 让类库的使用者 ...

  9. TJI读书笔记07-初始化

    TJI读书笔记07-初始化 成员初始化 构造方法初始化 初始化块 初始化的顺序 成员初始化 java尽量去保证每个变量在使用前都会得到初始化. 对于方法局部变量,java不会自动初始化他们,如果没有显 ...

随机推荐

  1. D 最熟悉的陌生人 (纪念当年就读的梅州市江南高级中学)

    最熟悉的陌生人 作者:张慧桥 “蝶恋花” 我匆匆地跟听众道了声再见,手忙脚乱地关掉了机器,拿出手机按下了一个快捷键…… “嘟…嘟…” 电话响两下后,我听到了那个我在睡梦中都可以认出来的声音. “你现在 ...

  2. [wxWidgets] 1. 安装及"hello world"程序

    关于wxWidgets的优越已经在它的官方网站有所阐述,本文不再赘述. 本系列主要记录学习这个软件包过程中遇到的问题以及心得. 1.  安装 从源码安装虽然大多时候不是一件轻松的过程,但是基于以下两个 ...

  3. nodejs单元测试

    前言: 之前一直听说过单元测试,但是具体怎么做,也没有深入研究,感觉测试是一件很麻烦的事,花费时间.可能是自己太懒了,一看到测试那么多陌生的东西就不想弄了. 然后一拖再拖,直到最近,换了一家公司,然后 ...

  4. 基于Autofac, Castle.DynamicProxy的动态WCF解决方案(原创)

    本方案解决了下面3个主要的问题: 1.减少配置,为了避免每次新增service都需要去修改配置文件,包括服务器端跟各个客户端的. 2.能够使用函数重载,泛型函数,以及泛型类. 3.使项目能够快速地在w ...

  5. Programming in Lua读书笔记

         Lua的长处之一就是可以通过新类型和函数来扩展其功能.动态类型检查最大限度允许多态出现,并自动简化调用内存管理的接口,因为这样不需要关心谁来分配内存谁来释放内存,也不必担心数据溢出.高级函数 ...

  6. Ejabberd 插件开发 --- IQ截获与处理

    ejabberd的组件开发其实是非常简单的,只要遵循其gen_mod规范,添加iq处理函数就可以了.下面一步步教大家如何开发ejabberd组件. 首先,最好是自己编译ejabberd源码,这样的话把 ...

  7. Ejabberd外部组件开发

    Ejabberd的基本介绍就不多言了,使用erlang开发的高并发高稳定性XMPP服务器,在whatsapp中得到了应用,算是erlang领域一个杀手级应用.前面的文章中我已经总结了Ejabberd插 ...

  8. MVC中使用内建的HTML辅助方法产生表单元素提交表单与button按钮事件的陷阱

    网站模板页有个登陆的退出按钮,当点击时跳转到登陆页面. <button onclick="logout()" >退出</button> $("#l ...

  9. java socket 通讯

    (转)http://blog.csdn.net/xn4545945/article/details/8098646

  10. Linux系统安全保护措施

    1.系统安全记录文件 操作系统内部的记录文件是检测是否有网络入侵的重要线索.如果系统是直接连接到Internet的,且发现有很多人对系统做telnet/FTP登录尝试,可以运行“#more/var/s ...