咱们用一段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/