[轉] Java內部類之閉包(closure)與回調(callback)

 閉包(closure)是一個可調用的對象,它記錄了一些信息,這些信息來自於建立它的做用域。經過這個定義,能夠看出內部類是面向對象的閉包,由於它 不只包含外圍類對象(建立內部類的做用域)的信息,還自動擁有一個指向此外圍類對象的引用,在此做用城內,內部類有權操做全部的成員,包括private 成員。javascript

 

   Java最引人爭議的問題之一就是,人們認爲Java應該包含某種相似指針的機制,以容許回調(callback)。經過回調,對象可以攜帶一些信息,這些信息容許它在稍後的某個時刻調用初始的對象。java

 

   稍後將會看到這是一個很是有用的概念。若是回調是經過指針實現的,那麼就只能寄但願於程序員不會誤用該指針。然而,您應該已經瞭解到,Java更當心仔細,因此沒有在語言中包括指針。程序員


   經過內部類提供閉包的功能是優良的解決方案,它比指針更靈活、更安全。見下例:編程

 // innerclasses/Callbacks.java
   // Using inner classes for callbacks
   package innerclasses
   import static net.mindview.util.Print.*;

   interface Incrementable {
      void increment();
   }

 

   // Very simple to just implement the interface
   class Callee1 implements Incrementable {
      private int i = 0;
      public void increment() {
         i++
         print(i);
      }
   }

 

   class MyIncrement {
      public void increment() {print("Other operation");}
      static void f(MyIncrement mi) {mi.increment();}
   }


   // If your class must implement increment() in
   // some other way, you must use an inner class:
   class Callee2 extends MyIncrement {
      private int i=0;
      public void increment() {
         super.increment();
         i++;
         print(i);
      }
      private class Closure implements Incrementable {
         public void increment() {
            // Specify outer-class method, otherwise
            // you'd get an infinite recursion
            Callee2.this.increment();
         }
      }
      Incrementable getCallbackReference() {
         return new Closure();
      }
   }

 

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

 

   public class Callbacks {
      public static void main(String[] args) {
         Callee1 c1 = new Calleel();
         Callee2 c2 = new Callee2();
         MyIncrement.f(c2);

         Caller caller1 = new Caller(c1);
         Caller caller2 = new Caller(c2.getCallbackReference());
         caller1.go();
         caller1.go();
         caller2.go();
         caller2.go();
      }
   }


輸出:設計模式

 

   Other operation
   1
   1
   2
   Other operation
   2
   Other operation
   3安全

 

   這個例子進一步展現了外圍類實現一個接口與內部類實現此接口之間的區別。就代碼而言,Callee1是簡單的解決方式。Callee2繼承自MyIncrement,後者已經有了一個不一樣的increment()方法,而且與Incrementable接口指望的increment()方法徹底不相關。閉包

 

   因此若是Callee2繼承了MyIncrement,就不能爲了Incrementable的用途而覆蓋increment()方法,因而只能使用內部類獨立地實現Incrementable。還要注意,當建立了一個內部類時,並無在外圍類的接口中添加東西,也沒有修改外圍類的接口。函數

 

   注意,在Callee2中除了getCallbackReference()之外,其餘成員都是private的。要想創建與外部世界的任何連 接,interface Incrementable都是必需的。在這裏能夠看到,interface是如何容許接口與接口的實現徹底獨立的。this

 

   內部類Closure實現了Incrementable,以提供一個返Callee2的「鉤子」(hook)——並且是一個安全的鉤子。不管誰得到此 Incrementable的引用,都只能調用increment(),除此以外沒有其餘功能(不像指針那樣,容許您作不少事情)。編碼

 

   Caller的構造器須要一個Incrementable的引用做爲參數(雖然能夠在任意時刻捕獲回調引用),而後在之後的某個時刻,(Caller對象可使用此引用回調Callee類。

 

   回調的價值在於它的靈活性——能夠在運行時動態地決定須要調用什麼方法。


以上來自:《java編程思想第三版》

動態語言的閉包是一個永恆的話題。閉包在編碼過程的方便和快捷使得動態語言的擁護者對它津津樂道,而靜態語言特別是Java語言的扇子們會拿出匿名內部類來講Java語言也有相似的功能。

 

JavaScript 中閉包的產生是因爲 JavaScript 中容許內部 function,也就是在一個 function 內部聲明的 function 。內部 function 能夠訪問外部 function 中的局部變量、傳入的參數和其它內部 function 。當內部 function 能夠在包含它的外部 function 以外被引用時,就造成了一個閉包。這個時候,即使外部 function 已經執行完成,該內部 function 仍然能夠被執行,而且其中所用到的外部 function 的局部變量、傳入的參數等仍然保留外部 function 執行結束時的值。下面是一個例子:

function Outer(){
    var i=0;
    function Inner(){
        alert(++i);
    }
    return Inner;
}
var inner = Outer();
inner();


由於函數Outer外的變量inner引用了函數Outer內的函數Inner,就是說:當函數Outer的內部函數Inner被函數Outer外的一個變量inner引用的時候,就建立了一個閉包。


閉包有什麼做用:簡而言之,閉包的做用就是在Outer執行完並返回後,閉包使得Javascript的垃圾回收機制GC不會收回Outer所佔用的資源,由於Outer的內部函數Inner的執行須要依賴Outer中的變量。


閉包是一個可調用的對象,它記錄了一些信息,這些信息來自於建立它的做用域。經過這個定義,能夠看出內部類是面向對象的閉包,由於它不只包含建立內部類的 做用域的信息,還自動擁有一個指向此外圍類對象的引用,在此做用域內,內部類有權操做全部的成員,包括private成員。

 

C++有指針函數,能夠實現回調。經過回調,對象可以攜帶一些信息,這些信息容許它在稍後的某個時刻調用初始的對象。Java中沒有指針,回調是經過匿名類來實現的。

 

 

>>>回調的一種理解<<<

 

回調的基本原理跟好萊塢原則同樣,Don't call me,I'll call you.


編程上來講,通常使用一個庫或類時,是你主動調用人家的API,這個叫Call,有的時候這樣不能知足須要,須要你註冊(注入)你本身的程序(好比一個對 象),而後讓人家在合適的時候來調用你,這叫Callback。設計模式中的Observer就是例子:全部的觀察者都須要向本身關心的主題 Observable註冊,而後主題在適當時機(主題類對象的屬性發生變化時)通知全部訂閱它的觀察者並更新,其中觀察者都實現了一個統一的 Observer接口中的Update方法。

回調實質上是指一個類儘管實際上實現了某種功能,可是沒有直接提供相應的接口,客戶類能夠經過這個類的內部類的接口來得到這種功能。而這個內部類自己並無提供真正的實現,僅僅調用外部類的實現。可見,回調充分發揮了內部類所具備的訪問外部類的實現細節的優點。

以上轉自:http://jiangzhengjun.iteye.com/blog/658354

相關文章
相關標籤/搜索