zoukankan      html  css  js  c++  java
  • Java 消息机制之回调详解

    1. 概述

    Java 中的回调机制是一个比较常见的机制,只是有可能在你的程序中使用得比较少,在一些大型的框架中回调机制随处可见。而在之前的博文《Java设计模式——观察者模式》及 Android 中对 ListView 的相关操作也有回调身影。本文就通过一些具体的实例,慢慢走近 Java 的回调机制。


    2. 版权说明

    著作权归作者所有。
    商业转载请联系作者获得授权,非商业转载请注明出处。
    本文作者:Q-WHai
    发表日期: 2016年4月24日
    本文链接:http://blog.csdn.net/lemon_tree12138/article/details/51231841
    来源:CSDN
    更多内容:分类 >> Thinking in java


    3. 目录


    4. 回调

    所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法。实际在使用的时候,也会有不同的回调形式,比如下面的这几种。

    4.1 同步回调

    这里我假设这样的一种情况。
    A 公司的总监 B 跟他的下属(项目经理 C)说要做一个调研,不过不用 C 自己亲力亲为。可以让经理 C 去安排他下面的程序员 D 去完成。
    经理 C 找到了程序员 D,并告诉他,现在要完成一个调研任务。并且把调研的结果告诉经理 C。如果有问题,还是要继续的。
    因为这里是 C 让 D 去做一件事情,之后 D 还是要将结果与 C 进行沟通。这样就是回调的模型了。下面是一般回调的类图:
    这里写图片描述

    首先我们要有一个回调的接口 CallbackInterface
    CallbackInterface.java

    public interface CallbackInterface {
        public boolean check(int result);
    }

    背景里,程序员 D 是要将结果与项目经理 C 进行沟通的,所以这里项目经理需要实现上面的回调接口:
    Manager.java

    public class Manager implements CallbackInterface {
    
        private Programmer programmer = null;
    
        public Manager(Programmer _programmer) {
            this.programmer = _programmer;
        }
    
        /**
         * 用于 Boss 下达的委托
         */
        public void entrust() {
            arrange();
        }
    
        // 进行安排下属进行 study 工作
        private void arrange() {
            System.out.println("Manager 正在为 Programmer 安排工作");
            programmer.study(Manager.this);
            System.out.println("为 Programmer 安排工作已经完成,Manager 做其他的事情去了。");
        }
    
        @Override
        public boolean check(int result) {
            if (result == 5) {
                return true;
            }
            return false;
        }
    
    }

    对于程序员 D 来说他需要持有一个经理 C 的引用,以便与他沟通。不过,这里是总监 B 让 经理 C 去安排的任务。也就是说这里也可以让其他的经理,比如说经理 B1, B2等等。因为经理都实现了回调的接口,所以这里就可以直接让程序员 D 持有这个接口就可以了。如下:
    Programmer.java

    public class Programmer {
    
        public void study(CallbackInterface callback) {
            int result = 0;
            do {
                result++;
                System.out.println("第 " + result + " 次研究的结果");
            } while (!callback.check(result));
    
            System.out.println("调研任务结束");
        }
    }

    对于总监来说就更简单明了了,因为这相当于一个 Client 测试:
    Boss.java

    public class Boss {
    
        public static void main(String[] args) {
            Manager manager = new Manager(new Programmer());
            manager.entrust();
        }
    }

    运行结果

    Manager 正在为 Programmer 安排工作
    第 1 次研究的结果
    第 2 次研究的结果
    第 3 次研究的结果
    第 4 次研究的结果
    第 5 次研究的结果
    调研任务结束
    为 Programmer 安排工作已经完成,Manager 做其他的事情去了。

    4.2 异步回调

    还是上面的例子,你的项目经理不可能要一直等你调研的结果。而是把这个任务交给你之后,他就不管了,他做他的,你做你的。所以,这里需要对回调的函数进行异步处理。
    所以,这里我们需要修改 Programmer 类的代码,修改如下:
    Programmer.java

    public class Programmer {
    
        public Programmer() {
        }
    
        public void study(CallbackInterface callback) {
            new StudyThread(callback).start();
        }
    
        // --------------------------- Programmer 正在做的工作 ---------------------------
    
        class StudyThread extends Thread {
    
            CallbackInterface callback = null;
    
            public StudyThread(CallbackInterface _callback) {
                callback = _callback;
            }
    
            @Override
            public void run() {
                int result = 0;
                do {
                    result++;
                    System.out.println("第 " + result + " 次研究的结果");
                } while (!callback.check(result));
    
                System.out.println("调研任务结束");
            }
        }
    }

    运行结果

    Manager 正在为 Programmer 安排工作
    为 Programmer 安排工作已经完成,Manager 做其他的事情去了。
    第 1 次研究的结果
    第 2 次研究的结果
    第 3 次研究的结果
    第 4 次研究的结果
    第 5 次研究的结果
    调研任务结束

    4.3 闭包与回调

    闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。

    这一部分的内容主要是参见于《Java 编程思想》一书,具体细节大家可以参见此书。
    可能很多人在阅读《Java 编程思想》的时候有一些小迷糊,迷糊的原因可能多种多样。只是书中的代码展示部分的确有一些头痛,没有什么结构可言(这个只是我个人的阅读习惯,无关书籍本身的经典性),所以阅读起来不是很给力吧。下面就我阅读时的一个小总结。

    4.3.1 普通调用

    首先,我们可以看看在正常情况下的调用是怎么进行的。
    Incrementable.java

    interface Incrementable {
        void increment();
    }

    这是一个普通的接口(在普通调用里只是普通接口,在回调中就是回调接口,这一点应该很好理解吧)。

    Callee1.java

    class Callee1 implements Incrementable {
    
        private int i = 0;
    
        @Override
        public void increment() {
            i++;
            System.out.println(i);
        }
    
    }

    Callbacks.java

    public class Callbacks {
        public static void main(String[] args) {
            Callee1 callee1 = new Callee1();
            callee1.increment();
        }
    }

    Callbacks 是一个测试客户端类,没啥好说的,直接看上面的代码。

    4.3.2 回调初试

    上面的普通调用也没啥好说的,因为这对于一个正常的 Java 程序员来说都应该是想都不用想就可以搞定的事情。
    现在如果要构成回调,那么对于程序的结构或是逻辑的思维上都不可能只有一个被调用者(被回调的对象 Callee1),还需要一个调用者对象。调用者可以像下面这样来编写:
    Caller.java

    class Caller {
    
        private Incrementable callbackReference;
    
        public Caller(Incrementable _callbackReference) {
            callbackReference = _callbackReference;
        }
    
        void go() {
            callbackReference.increment();
        }
    }

    这里 Caller 持有一个回调接口的引用 callbackReference,就像在上面说到的程序员需要持有一个项目经理的引用,这样就可以通过这个引用来与项目经理沟通。这里的 callbackReference 也正是起到了这个作用。
    现在我们来看看测试类的编写:
    Callbacks.java

    public class Callbacks {
        public static void main(String[] args) {
            Callee1 callee1 = new Callee1();        
            Caller caller1 = new Caller(callee1);
            caller1.go();
        }
    }

    对于到目前为止的程序代码,完全可以对比上面项目经理安排程序员调研技术难题的代码。有异曲同工之妙。

    4.3.3 闭包回调

    相比于正常的回调,闭包回调的核心自然是在于闭包,也就是对作用域的控制。
    现在假设有一个用户(其他程序员)自定义了一个 MyInCrement 类,同时包含了一个 increment 的方法。如下:

    class MyInCrement {
    
        public void increment() {
            System.out.println("MyCrement.increment");
        }
    
        static void f(MyInCrement increment) {
            increment.increment();
        }
    }

    另外有一个类 Callee2 继承自上面这个类:

    class Callee2 extends MyInCrement {
    
        private int i = 0;
    
        public void increment() {
            super.increment();
            i++;
            System.out.println(i);
        }
    }

    显而易见这里如果要调用 increment() 方法,就变成了一般的函数调用了。所以这里我们需要修改上面的 Callee2 类,修改的目标就是让 Callee2 类可以兼容 MyInCrement 类的 increment() 方法和 Incrementable 的 increment() 方法。修改后:

    class Callee2 extends MyInCrement {
    
        private int i = 0;
    
        public void increment() {
            super.increment();
            i++;
            System.out.println(i);
        }
    
        private class Closure implements Incrementable {
    
            @Override
            public void increment() {
                Callee2.this.increment();
            }
        }
    
        Incrementable getCallbackReference() {
            return new Closure();
        }
    }

    注意,这里的 Closure 类是一个私有的类,这是一个闭包的要素。因为 Closure 类是私有的,那么就要有一个对外开放的接口,用来对 Closure 对象的操作,这里就是上面的 getCallbackReference() 方法。 Caller 类则没有改变。
    对于测试客户端就直接看代码吧:

    public class Callbacks {
        public static void main(String[] args) {        
            Callee2 callee2 = new Callee2();
            Caller caller2 = new Caller(callee2.getCallbackReference());
            caller2.go();
        }
    }

    5. Ref


  • 相关阅读:
    swoole 安装方法 使用即时聊天
    git的介绍以及简单应用
    curl的应用
    linux下监听和同步代码配置
    mac skim 修改背景色
    php 编译安装的一个 configure 配置
    mac mysql error You must reset your password using ALTER USER statement before executing this statement.
    yii2 控制器里 action 大小写组合造成的路由问题
    warning : json_decode(): option JSON_BIGINT_AS_STRING not implemented in xxx
    redis 自启动脚本
  • 原文地址:https://www.cnblogs.com/fengju/p/6335993.html
Copyright © 2011-2022 走看看