咱們用一段gradle的腳本作引子,理解這一段腳本與通常的groovy代碼是怎麼聯繫起來的html
buildscript { repositories { jcenter() mavenLocal() //或者使用指定的本地maven 庫 maven{ url "file://D:/repo" } } dependencies { classpath 'com.android.tools.build:gradle:1.2.3' } }
DSL的定義 java
DSL(Domain Specific Language)定義:針對某一領域,具備受限表達性的一種計算機程序設計語言。android
所謂針對某一領域,其基本思想是「求專不求全」,不像通用目的語言那樣目標範圍涵蓋一切軟件問題,而是專門針對某一特定問題的計算機語言。編程
DSL伴隨語義模型出現,語義模型會表現爲程序庫或者框架,對於構建DSL而言,語義模型不可或缺。DSL只是位於其上的一層而已。定義作什麼,而不是用一堆命令語句來描述怎麼作,因此它是聲明式編程(如SQL),這一點很重要。DSL的受限表達性可使DSL語言不易出錯,即使出錯,也易於發現。這是受限表達性的意義。閉包
DSL是通用語言的特定用法。內部DSL一般是一段合法的程序,可是具備特定的風格。並且只用到了語言一部分特性。防止DSL逐漸演變爲一種通用語言,要受限表達。目的防止DSL過於複雜,可維護性下降,學習成本提高,偏離方向。不要讓DSL讀起來向天然語言。它是程序語言,比天然語言更加準確和簡潔。語義模型位於語言和DSL之間,爲兩者解耦。DSL腳本,解析器,語義模型,模型——DSL自上而下幾個層次。框架
咱們先看一個簡單的類和一個奇葩的語法現象:分號省略;默認public省略;有形參的方法調用,括號能夠省略;返回的return能夠省略,默認最後一行代碼的值返回。maven
import groovy.xml.* import java.io.* class Task{ //默認省略public和分號 String summary String description Date dueDate Map m static void main(args){ //默認有set和get方法 Task task1 = new Task() task1.setSummary("this is Task1") println task1.getSummary() //有形參的方法調用,括號能夠省略 task1.setDescription "this is Task class" task1.printDescription "" //能夠直接傳map Task task3 = new Task() task3.setM (['summary':'this is Task3','description':'Task']) println task3.getM() //map的分號能夠省略 Task task2= new Task('summary':'this is Task2','description':'Task') println task2.getSummary() //括號也能夠省略 Task task4 = new Task() task4.setM 'summary':'this is Task4' println task4.getM() } public void printDescription(def str){ println "the task description is : $description" } }
看完省略括號的語法現象,下面看另外一個重量級的語法現象——閉包函數
閉包是用{符號括起來的代碼塊,它能夠被單獨運行或調用,也能夠被命名。相似‘匿名類’或內聯函數的概念。學習
閉包中最多見的應用是對集合進行迭代,下面定義了3個閉包對map進行了迭代:gradle
map.each({key,value-> //key,value兩個參數用於接受每一個元素的鍵/值
println "$key:$value"})
map.each{println it} //it是一個關鍵字,表明map集合的每一個元素
map.each({ println it.getKey()+"-->"+it.getValue()})
除了用於迭代以外,閉包也能夠單獨定義:
def say={word->
println "Hi,$word!"
}
調用:
say('groovy')
say.call('groovy&grails')
輸出:
Hi,groovy!
Hi,groovy&grails!
看起來,閉包相似於方法,須要定義參數和要執行的語句,它也能夠經過名稱被調用。然而閉包對象(不要奇怪,閉包也是對象)能夠做爲參數傳遞(好比前面的閉包做爲參數傳遞給了map的each方法)。而在java中,要作到這一點並不容易(也許C++中的函數指針能夠,但不要忘記java中沒有指針)。其次,閉包也能夠不命名(固然做爲代價,只能在定義閉包時執行一次),而方法不能夠。
當閉包遇到括號省略,一切都不同了
Project.groovy
public class Project{ Date date void setDateFormat(Closure formatDate){ println formatDate(date) } }
Main.groovy
public class Main{ public static void main(def args){ Project p = new Project() p.setDate new Date() //正常 p.setDateFormat({ return it.format('yyyy-MM-dd HH:mm:ss') }) //減return p.setDateFormat { it.format('yyyy-MM-dd HH:mm:ss') } //減括號(是否是很像咱們的gradle腳本?) p.setDateFormat { it.format 'yyyy-MM-dd HH:mm:ss' } } }
減完以後,像不像下面的腳本?
dependencies { classpath 'com.android.tools.build:gradle:1.2.3' }
惟一的區別是咱們須要用對象來引用方法,其實去掉對象也不難,本身調用本身的方法就能夠了,看下面的代碼:
public class Project{ Date date void setDateFormat(Closure formatDate){ println formatDate(date) } //將format內置 String format(String f){ date.format(f) } //無形參的方法 void showDate(){ print date.toString() } void run(){ //對象去掉,是否是如出一轍了? setDateFormat{ println 'this is a scrip' format 'yyyy-MM-dd HH:mm:ss' //沒有形參的話就只能乖乖寫括號了 showDate() } } }
DSL的兩個關鍵點,某一領域,gradle只爲編譯,也只用於編譯。受限表達,通常只調用腳本運行上下文環境中的方法,爲的就是儘可能簡單,出錯的話,排錯方便。伴隨而生的語義模型就是那一套編譯框架。
Groovy對DSL的支持,表現爲能夠省略:分號,調用方法的括號,return,默認public等。
參考:
http://docs.groovy-lang.org/latest/html/documentation/