詳解Java的回調機制_java

來源:互聯網
上載者:User

模組之間總是存在這一定的介面,從調用方式上看,可以分為三類:同步調用、回調和非同步呼叫。下面著重詳解回調機制。

1. 概述

Java 中的回調機制是一個比較常見的機制,只是有可能在你的程式中使用得比較少,在一些大型的架構中回調機制隨處可見。本文就通過一些具體的執行個體,慢慢走近 Java 的回調機制。

2.回調

所謂回調:就是A類中調用B類中的某個方法C,然後B類中反過來調用A類中的方法D,D這個方法就叫回調方法。實際在使用的時候,也會有不同的回調形式,比如下面的這幾種。

2.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 做其他的事情去了。

2.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 次研究的結果
調研任務結束

2.3 閉包與回調

閉包(closure)是一個可調用的對象,它記錄了一些資訊,這些資訊來自於建立它的範圍。

2.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 是一個測試用戶端類,沒啥好說的,直接看上面的代碼。

2.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();  }}

對於到目前為止的程式碼,完全可以對比上面專案經理安排程式員調研技術難題的代碼。有異曲同工之妙。

2.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個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.