E.1 Groovy语言的DSL相关特性

Groovy是一种动态类型的OO语言,拥有强大的反射式元编程和生成式元编程能力。Groovy与Java语言共享对象模型,因此它们之间的互操作性十分优秀。Groovy还可以作为一种脚本语言使用。Groovy比较重要的语言特性包括可选的类型声明、运算符重载、便捷丰富的字面量语法,以及闭包等函数式抽象。表E-1简要概括了那些令Groovy成为一种优秀的DSL实现语言的重要特性。

表E-1 Groovy语言特性汇总

基于类的OOP
Groovy是一种面向对象的语言。我们可以定义类以及类中的实例变量和方法。类的定义语法与Java类似,不过省略不写的可见性修饰符会被默认为public。类定义语法的详情请参阅第E.2节文献[1]
  1. class Account {
  2. Integer balance(Date date) = {
  3. //.. 实现
  4. }
  5. //..
  6. }
上面是Groovy类的声明片段
可选的类型声明
我们可以像使用Java语言那样静态地为运行时声明类型;也可以用def关键字来代替类型声明,从而获得像Python语言那样的动态类型效果。方法和闭包的形参甚至连def关键字都可以省略不写
  1. String str = new String("Groovy");
  2. str = 8
  3. def dstr = "dynamic"
  4. dstr = 20
str将被赋值String类型的the String 8dstr将被赋值Integer类型的the Integer 20
属性
不管什么类型的字段,只要省略不写该字段的可见性修饰符,就相当于声明了一个属性
  1. class Foo {
  2. String str
  3. def dyn
  4. }
这是一个含有属性的类
字符串
我们可以定义单行的字符串、多行的字符串,以及可以内嵌占位符的GString
  1. def single = '这是单行字符串'
  2. def multi = """ 这是多行字符串"""
  3. def gstring = "$single 一共有 ${single.size} 个字符"
这段代码展示了Groovy支持的几种字符串
集合数据类型
Groovy提供了各种常用的集合数据类型,如Range、List、Map,等等。它们各自都有十分简便的字面量定义语法,尤其适合用DSL脚本
  1. // (半开)区间
  2. (0..<10).each { println it }
  3. // 各种列表操作
  4. [1,2,3] 2 == [1,2,3,1,2,3]
  5. [1,[2,3]].flatten() == [1,2,3]
  6. [1,2,3].reverse() == [3,2,1]
  7. [1,2,3].disjoint([4,5,6]) == true
  8. // map定义的字面量语法
  9. def map = [a:0, b:1]
上面是Groovy语言中各种集合数据类型的例子
闭包
闭包是一种可以在适当时机具体化(reification)之后执行的代码块。闭包内封装了一段逻辑,也封装了包围这段逻辑的作用域
  1. def clos = { println "hello world!" }
  2. clos() // 结果打印输出“hello world!”
  3. def mult = {x, y -> println x y}
  4. mult(2, 5) //结果打印输出 10
上面是Groovy闭包的一些简单的使用片段
建造器(Builder)
Builder可以用惊人简洁的语法构造出复杂的层级数据模型。其中的秘诀是元编程
  1. def builder = new
  2. groovy.xml.MarkupBuilder(writer)
  3. builder.html(){
  4. head(){
  5. title("Welcome"){}
  6. }
  7. body(){
  8. p(“How are you?”)
  9. }
  10. }
Groovy建造器的实现原理结合了元编程和闭包
元编程——ExpandoMetaClass
ExpandoMetaClass是Groovy最重要的元编程构造之一,它允许我们按照闭包的定义语法,动态地添加方法、构造器、属性和静态方法
  1. Integer.metaClass.twice << {delegate 2}
这段代码向Integer类添加了一个名为twice的方法。新的方法在不受限制的作用域内对所有的线程可见
元编程——Category特性
Category概念的作用与ExpandoMetaClass类似,但可以将动态注入内容的可见性限制在我们明确指定的作用域内。
  1. class IntegerCategory {
  2. static Integer twice(Integer i) {
  3. return i 2
  4. }
  5. }
  6. use (IntegerCategory) {
  7. assert 4 == 2.twice()
  8. }
twice方法仅在use {}划定的作用域内可见