zoukankan      html  css  js  c++  java
  • TJI读书笔记14-闭包与回调

     

     

     

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

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

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

    • 内部类可以有多个实例,每个实例都有自己的状态信息. 并且与外嵌类对象的信息相互独立.
    • 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口.
    • 内部类是一个独立的实体,不会存在”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这个类的实例手里有它外嵌类的引用. 所以它知道它的作用域,也就是这个外嵌类实例的信息. 我的个人理解,不知道对不对.

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

    .1474386153799
    图片来自维基百科.

    比如酒店的叫醒服务. 酒店(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类里还是一个匿名内部类里不重要. 画了一个聊胜于无的图…我觉得我貌似看懂了…

    image

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

  • 相关阅读:
    datanode报错Problem connecting to server
    使用命令查看hdfs的状态
    Access denied for user root. Superuser privilege is requ
    ElasticSearch默认的分页参数 size
    SparkStreaming Kafka 维护offset
    【容错篇】Spark Streaming的还原药水——Checkpoint
    251 Android 线性与相对布局简介
    250 Android Studio使用指南 总结
    249 如何解决项目导入产生的中文乱码问题
    248 gradle更新问题
  • 原文地址:https://www.cnblogs.com/thecatcher/p/5891164.html
Copyright © 2011-2022 走看看