groovy中的一個核心文法:closurs,也叫閉包。閉包在groovy中是一個處于代碼上下文中的開放的,匿名代碼塊。它可以通路到其外部的變量或方法。
其中<code>[]</code>内是可選的閉包參數,可省略。當閉包帶有參數,就需要<code>-></code>來将參數和閉包體相分離。
下面看一些閉包的具體例子:
閉包在groovy中是<code>groovy.lang.Closure</code>類的執行個體,這使得閉包可以指派給變量或字段。
閉包有兩種調用方式:
閉包名+()或者閉包名.call()來調用閉包。
閉包的參數類型和前面講的方法的參數類型一樣,這裡不多說。
當閉包沒有顯式聲明參數時,其預設包含一個隐式的參數<code>it</code>。
參數清單的用法與普通方法一樣,這裡不多贅述。
委托政策是groovy中閉包獨有的文法,這也使得閉包較java的lambda更為進階。下面簡單介紹一下groovy中的委托政策。
在了解delegate之前,首先先要了解一下閉包中this和owner的含義,閉包中三者是這麼定義的:
<code>this</code> 表示定義閉包的外圍類。
<code>owner</code> 表示定義閉包的直接外圍對象,可以是類或者閉包。
<code>delegate</code> 表示一個用于處理方法調用和屬性處理的第三方類。
閉包中,使用<code>this</code>關鍵字或者調用方法<code>getThisObject()</code>來獲得其外圍類:
判斷this表示的具體是哪個對象可以從this往外找,遇到的第一類就是this代表的類。
owner與this類似,隻不過owner表示的是直接外圍對象,可以是類也可以是閉包:
上述例子與this中的例子不同的就是NestedClosures,其中owner表示的是nestedClosures而不是NestedClosures。
閉包中可以使用delegate關鍵字或者getDelegate()方法來得到delegate變量,它預設與owner一緻,但可以由使用者自定義其代表的對象。
閉包中的delegate可被指向任意對象,我們看下面這個例子:
定義了兩個擁有相同屬性name的類Person和Thing。接着定義一個閉包,其作用是通過delegate來獲得name屬性。
接着改變閉包的delegate的指向,我們可以看到閉包調用結果也不同:
在閉包中,當一個屬性沒有指明其所有者的時候,delegate政策就會發揮作用了。
可以看到處的name沒有指明其所有者。即這個name屬性壓根不知道是誰的。在處指明cl的delegate為p,這時候在處調用成功。
以上代碼之是以可以正常運作是因為name屬性會被delegate處理。這是一個十分強大的方式用于解決閉包内的屬性的通路或方法的調用。在處沒有顯示的使用delegate.name是因為delegate政策已經在程式運作的時候幫助我們這樣做了。下面我們看看閉包擁有的不同的delegate政策:
<code>Closure.OWNER_FIRST</code> 這是預設的政策,優先從owner中尋找屬性或方法,找不到再從delegete中尋找。上面的例子就是因為在owner中沒有找到name,接着在delegate中找到了name屬性。
<code>Closure.DELEGATE_FIRST</code> 與OWNER_FIRST相反。
<code>Closure.OWNER_ONLY</code> 隻在owner中尋找。
<code>Closure.DELEGATE_ONLY</code> 隻在delegate中尋找。
<code>Closure.TO_SELF</code> 在閉包自身中尋找。
下面我們看一下預設的Closure.OWNER_FIRST的用法:
盡管在處将delegate指向了t,但因為是owner first的緣故,還是會優先使用Person的name屬性。
略做修改:
這時候就會通路t的name屬性了。
下面再來看一個例子:
當使用了Closure.DELEGATE_ONLY後,若delegate中找不到age屬性,則會直接報錯。
先來看一下下面這段代碼:
OK,運作沒有問題,那如果加兩行代碼呢?
這裡就會報錯了,錯誤原因有兩:
GString隻是調用了字元串的toString方法來獲得值。
<code>${x}</code>這種寫法并不是一個閉包,而是一個表達式等價于<code>$x</code>,當GString被建立的時候該表達式會被計算。
是以當給x指派2的時候,gs已經被建立,表達式也已經被計算,結果是x = 1,是以gs得值就是固定的x = 1。
如果要在GString使用閉包也是可以的,如下:
def x = 1def gs = "x = ${-> x}"assert gs == 'x = 1'x = 2assert gs == 'x = 2'
本文轉自 sshpp 51CTO部落格,原文連結:http://blog.51cto.com/12902932/1928051,如需轉載請自行聯系原作者