天天看點

詳解Java中的閉包(Closure)

以前總是對閉包(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是上面三個類和一個接口的類圖:

​​

詳解Java中的閉包(Closure)

​​

圖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();