前言:
Java的閉包與回調我也是第二次接觸這個概念,之前在自學Android的時候繪制View很多地方都用到了監聽器回調,一直不是很明白,現在回頭鞏固Java的基礎總算的弄明白,盡量用我自己了解的語言來和大家分享,希望對不懂的朋友可以有一定的幫助,大神也可以給我一點指點。
概念:
我覺得在了解一個事物之前,需要對這個事物在我們的大腦裡有一個初步的概念,然後再對這個概念補充上細節,這是我在了解一些陌生事物的時候的一個方法,也可以說是類比了解法吧。先說閉包~
一.閉包
閉包,故名思意就是,把一個包關起來,那麼對于Java來說,這個包就是類了,因為在java中任何事物都是類,都是對象。那麼閉包,直接了解上就是把一個類封裝起來(封裝就是包裝差不多的意思)。然後結合一下,閉包内容放在内部類中,是以閉包就是用一個類把另一個類包裝起來,說起來這和内部類沒有什麼不同點啊,為啥要專門用一個詞來表述它呢?因為這個閉包還有很多其他的作用。而且其構造要比内部類複雜一點,先說說它的作用,作用有二~
1. 閉包能夠保護内部類裡面的變量安全,不會被外部通路
2. 閉包能夠維持一個變量一直存活在記憶體中,不被CG(垃圾回收機制)回收掉
在構造上,内部類需要提供一個給外部調用它的接口,這樣才能在維持住内部類的同時,因為内部類攜帶了外部類的資訊,是以外部類也得以存活。
二.回調
回調直接了解就是回頭調用,先将相關的方法實作好,但是并不由我來決定什麼時候來調用它,而是等到一個時候,程式自己回頭調用這個方法,而實作回調機制,這可以說是一種設計模式,而不僅僅是一個語言上的特性。與回調相關的概念還有同步調用,與異步調用,同步調用即單向調用,調用方等待對方執行完成後才傳回。異步調用則類似消息機制,等待收到一定的消息後執行某些操作,回調與異步調用有一些共同之處,現在了解的還不是很清楚,先埋下一坑,以後清楚了在補一篇。
執行個體:
接下來就直接上代碼分析,讓大家補充一些細節上的了解,來清楚整個過程,整個過程相當于我的口述,如有不對的地方,希望大家指出:
interface Incrementable{
void increment();
}
class Callee1 implements Incrementable{
private int i = ;
public void increment(){
i++;
System.out.println(i);
}
}
class MyIncrementable {
public void increment(){ System.out.println("Other Operarion"); }
static void f(MyIncrementable mi){ mi.increment(); }
}
class Callee2 extends MyIncrementable{
private int i = ;
public void increment(){
super.increment();
i++;
System.out.println(i);
}
private class Closure implements Incrementable{
public void increment(){
Callee2.this.increment();
}
}
Incrementable getCallbackReference(){
return new Closure();
}
}
class Caller{
private Incrementable callbackReference;
Caller(Incrementable cbn){ callbackReference = cbn; }
void go(){ callbackReference.increment(); }
}
public class Callbacks {
public static void main(String[] args){
Callee1 c1 = new Callee1();
Callee2 c2 = new Callee2();
MyIncrementable.f(c2);
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}
這個是java程式設計思想上很經典的一個例子。輸出是這樣的:
Other Operarion
1
1
2
Other Operarion
2
Other Operarion
3
希望大家首先自己通讀一下代碼,然後了解一下程式輸出結果為什麼是這樣的,這樣有助于我們去了解之前所說的閉包與回調機制。
這裡我預設大家看完了,我來說一下我的了解:
首先Callee1是一個簡單的實作了接口Incrementable與相關方法,在這裡起到一個對比的作用而已。然後實作了一個MyIncrement類同樣實作了一個increment()方法但是這個與接口中的increment()沒有任何關系,因為這個類自己實作的,并沒有實作這個接口,而靜态方法f()也隻是為了測試一下increment()方法。而Callee2繼承自這個類。這裡就是重點了。同樣寫了一個increment()方法,覆寫了父類方法,但是中間還是調用了父類方法。接下裡是一個内部類也就是閉包的具體實作了。内部類實作了接口Incrementable并且直接調用外部類的方法作為具體的實作。内部類實作Increment able接口很關鍵,這樣就給外部留下了一個通道,能夠接受這個内部類。最後Callee2的後面留下了一個鈎子,即getCallbackReference()方法,它傳回一個内部類的對象,實作了内部與外部的連結,同時有保證了内部類的安全,因為隻有Callee2的對象可以通路與調用這個内部類的方法,而其他的類都無權通路,即使是基類接口對象。而後面的Caller類起到的是一個喚醒作用,通過接受不同的接口對象,實作不同的操作,但還有一個作用是等待接受一個内部類對象,來産生回調。現在大家再回頭看一下輸出就能夠明白了。
假裝你回頭看了,在main()方法中,首先是建立對象與聲明,然後是調用了一個MyIncrement的靜态方法,傳入的是一個Callee2對象,此時無法觸發回調,是以隻是正常的輸出,然後,才Caller2的初始化時傳入的是一個Closure對象進而産生了回掉。
以上就是java的閉包與回調機制,結合後面的内容會有更多意想不到的作用~