以前總是對閉包(Closure)一知半解的,最近花時間查閱資料,很多地方都講得語焉不詳,看後仍感覺雲裡霧裡的。後來還是研究了下李剛寫的《瘋狂Java講義》,裡面講得還是比較清楚,才終于弄清楚了閉包是怎麼回事。現在分享研究結果出來,也算是給像我原來一樣有疑惑的學習者一個參考。
閉包(Closure)是一種能被調用對象,它儲存了建立它的作用域的資訊。
Java并不能顯式地支援閉包,但對于非靜态内部類而言,它不僅記錄了其外部類的詳細資訊,還保留了一個建立非靜态内部類對象的引用,并且可以直接調用外部類的private成員,是以可以把非靜态内部類當成面向對象領域的閉包。
通過這種仿閉包的非靜态内部類,可以很友善地實作回調功能,回調就是某個方法一旦獲得了内部類對象的引用後,就可以在合适時候反過來調用外部類的方法。所謂回調,就是允許客戶類通過内部類引用來調用其外部類的方法,這是一種非常靈活的功能。
假設有下面的Teachable接口和Programmer基類,它們都提供了一個work方法,這兩個方法的方法簽名完全相同,但方法功能可能不一樣。
interface Teachable
{
void work();
}
public class Programmer
{
private String name;
//Programmer類的兩個構造器
public Programmer(){}
public Programmer(String name)
{
this.name = name;
}
//此處省略了name屬性的setter和getter方法
...
public void work()
{
System.out.println(name + "在燈下認真敲鍵盤...");
}
}
假設現在有一個人,既是一個程式員,也是一個教師。也就是說需要定義一個特殊的類,既需要實作Teachable接口,也需要繼承Programmer父類。表面上看起來這沒有任何問題,問題是Teachable接口和Programmer父類裡包含了相同的work方法,如果采用如下代碼來定義一個特殊的TeachableProgrammer類:
public class TeachableProgrammerextends Programmerimplements Teachable
{
public void work()
{
System.out.println(super.name + "教師在講台上講解...");
}
}
顯然上面的TeachableProgrammer類隻有一個work方法,這個work方法隻能進行“教學”,不再可以進行“程式設計”。但實際需要TeachableProgrammer類裡既包含“教學”的work方法,也包含“程式設計”的work方法。
這個時候,可以通過一個仿閉包的内部類來實作這個功能:
public class TeachableProgrammer extends Programmer
{
public TeachableProgrammer(){}
public TeachableProgrammer(String name)
{
super(name);
}
//教學工作依然由TeachableProgrammer類定義
private void teach()
{
System.out.println(getName() + "教師在講台上講解...");
}
private class Closure implements Teachable
{
public void work()
{
teach();
}
}
//傳回一個非靜态内部類引用,允許外部類通過該非靜态内部類引用來回調外部類的方法
public TeachablegetCallbackReference()
{
return new Closure();
}
}
上面的TeachableProgrammer隻是Programmer類的子類,它可以直接調用Programmer基類的work方法,該類也包含教學的teach方法,但這個方法與Teachable接口沒有任何關系,TeachableProgrammer也不能當成Teachable使用。此時建立了一個Closure内部類,它實作了Teachable接口,并實作了教學的work方法(粗體字部分)——但這種實作是通過回調TeachableProgrammer類的teach方法實作的。如果需要讓TeachableProgrammer對象進行教學,隻需調用Closure内部類(它是Teachable接口的實作類)對象的work方法即可。
TeachableProgrammer類提供了一個擷取内部類對象的方法:該方法無需傳回Closure類型,隻需傳回所實作接口:Teachable類型即可,因為它隻需要當成一個Teachable對象使用即可。
下面圖1是上面三個類和一個接口的類圖:
圖1:類圖
圖1清楚地看出Closure内部類的作用,它可實作Teachable接口,也可以當成Teachable使用,而且它是TeachableProgrammer的内部類,是回調TeachableProgrammer對象方法的入口,它的work方法實際上回調了TeachableProgrammer類的teach方法。
下面程式示範了如何讓TeachableProgrammer對象既執行“教學”的work方法,也執行“程式設計”的work方法。
public class TestTeachableProgrammer
{
public static void main(String[] args)
{
TeachableProgrammer tp = new TeachableProgrammer("李剛");
//直接調用TeachableProgrammer類從Programmer類繼承到的work方法
tp.work();
//表面上調用的是Closure的work方法,實際上是回調TeachableProgrammer的teach方法
tp.getCallbackReference().work();