Groovy基础语法

字符串

引号

Groovy支持单引号(’’),双引号(””)和三引号(” “ “)来封装字符串。其中三引号表示的字符串可以包含多行文本。

1
2
3
4
def a = """Spread
over
four"""
print(a)

输出结果如下:

1
2
3
Spread
over
four

使用单引号封装的字符串的值就是所列出的字符序列本身,另外两种形式的字符串的值可能会进一步被解释,任何包含在解释型字符串中的${expression}都将被求值,其结果是字符串的一部分。

1
2
3
4
5
def age = 25
'My age is ${age}' // My age is ${age}
"My age is ${age}" // My age is 25
"""My age is ${age}""" // My age is 25
"My age is \${age}" // My age is ${age}

Groovy的通用原则是只在字符串需要被解释的时候使用双引号,其他情况下使用单引号。Groovy中也使用字符反斜线\来进行转义。

###正则表达式

Groovy支持使用~”regex”来定义正则表达式,通过下面的方法可以创建一个正则表达式:

1
def regex =~ 'cheese'

在if语句或者while语句中,当Groovy操作符”=~”作为一个谓词出现时,左边的操作数String对象将和右边的正则表达式匹配。下面所有的表达式结果均为true:

1
2
3
def regex =~ 'cheese'
'cheesecake' =~ 'cheese'
'cheesecake' =~ regex

“==~”是精确匹配符。

在正则表达式中,脱字符号(^)和美元符号($)分别表示某行的开始和结尾。

1
2
3
def rhyme = 'a b c'
rhyme =~ '^a' // true
rhyme =~ 'c$' // true

加号(+)表示在表达式中位于它前面的字符出现一次或者多次,星号(*)表示出现零次或者多次。”{“和 “}”用来匹配位于”{“之前指定次数的字符,下列表达式的结果均为true:

1
2
'aaaaab' =~ 'a * b'
'aaaaab' =~ 'a{5}b'

列表和映射

列表

索引

列表是一种用来储存数据项集合的数据结构。在Groovy中,列表字面值是一系列包含在方括号中的对象集合,这些对象用逗号分隔。

Groovy列表使用索引操作符[]来标识元素值。列表的索引从0开始,指向列表的第一个元素。

1
2
3
def numbers = [11, 12, 13, 14]	// 列表含有四个元素
numbers[0] // 第0个元素的值为11
numbers[3] // 第3个元素的值为14

如果整形索引为负数,则其引用的元素从列表末端开始向前移动。

1
2
numbers[-1]  // 值为14
numbers[-2] // 值为13

索引段同样可以用于列表。

1
[11, 12, 13, 14][2]  // 值为13

也可以通过索引范围来操作列表。

1
2
numbers[0..2]  // 返回列表[11, 12, 13]
numbers[1..<3] // 返回列表[12, 13]

操作符

主要使用以下操作符对列表进行操作。

  • <<
  • +
  • -
1
2
def a = [1]
a << 2 // 返回列表[1, 2]

映射

映射(也就是大家熟知的组合数组,词典,表格和散列)是一种引用对象的无需集合。映射中的所有元素都可以通过关键字访问,它使用的关键字可以是任意类型的。

在映射字面值中,如果某个元素的关键字是一个变量名,那么它将被解释成一个String。

1
2
3
def x = 1
def y = 2
def m = [x : y, y : x]

于是,m就是映射:

m = [‘x’ : 2, ‘y’ : 1]

范围

范围是表达特定序列值的一种简略方法。它通过序列中第一个值和最后一个值表示,范围还具有包含和不包含关系。

1
2
3
1900..1999  // 包含边界
1900..<2000 // 不包含边界
'A'..'D' // A,B,C,和D

包含边界的范围通过..表示,而不包含边界的范围通过..<

Groovy也为范围定义了一些方法。

  • get
  • getFrom (获取当前范围中下标最小的元素值)
  • getTo (获取当前范围中下标最大的元素值)
  • isReverse
  • size
  • subList
1
2
3
4
5
6
7
8
9
10
def twentiethCentury = 1900..1999
def reversedTen = 10..1
twentiethCentury.size // 100
twentiethCentury.get(0) // 1900
twentiethCentury.getFrom() // 1900
twentiethCentury.getTo() // 1999
twentiethCentury.contains(2000) // false
twentiethCentury.subList(0, 5) // 1900..1904
reversedTen[2] // 8
reversedTen.isReverse() // true

流程控制

Groovy提供了一些用来改变程序逻辑的流程控制语句,可以将它们分为三种流程控制结构:

  • 顺序
  • 选择
  • 迭代

while语句

最基本的迭代子句是while语句。

for语句

for语句用于循环处理某个范围内,集合(列表,映射或者数组)或者字符串。

if语句

if语句大家应该都很熟悉,此处不再赘述。

switch语句

一连串频繁出现的if-else语句,可以通过一个switch语句来实现。

Switch和范围

case表达式即可以表示某个整型值,也可以表示整型值的范围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import console.*
print 'Enter examination score:'
def score = Console.readInteger()
def grade
switch(score){
case 70..100:
grade = 'A'
break
case 60..69:
grade = 'B'
break
case 50..59:
grade = 'C'
break
case 40..49:
grade = 'D'
break
case 0..39:
grade = 'E'
break
}
println "Score:${score};grade:${grade}"

输出结果为:

Enter examination score: 50
Score: 50; grade: C

break语句

break语句通常用来改变循环语句和switch语句中的控制流程。在循环结构体内执行break语句时,将立即终止循环体的最内层循环。

continue语句

continue语句是break语句的补充,仅限于在while和for循环中使用。

当执行continue语句时,将结束本次循环,并跳转到离它最近的条件判断语句,以确认是否执行下一个循环。

在含有continue语句的循环中,程序将跳过循环体内下面所有尚未执行的语句。

闭包

闭包的调用

1
2
def clos = {println 'Hello world'}
clos.call()

这段代码的含义是使用标识符clos引用这个闭包,使用call来执行这个标识符所引用的代码块。

在闭包声明中引入形参。

1
2
3
4
def clos = {param -> println "Hello ${param}"}
clos.call('world')
clos.call('again')
clos('shortcut')

输出结果为:

Hello world
Hello again
Hello shortcut

在第三个语句中我们会可以看到,call是可以省略的。

闭包,集合和字符串

许多列表,映射和字符串方法都接受闭包参数。

each方法和闭包

each方法的原型为void each(Closure closure),它常用于列表,映射和字符串,以遍历每个元素,并将闭包应用于每个元素。

1
[1, 2, 3, 4].each {println it}

其输出结果为:

1
2
3
4
5

find方法和闭包

find方法返回集合中符合某个判断标准的第一个值。在闭包中,集合元素使用的判断条件必须是布尔表达式。当存在符合条件的值时,find方法将返回第一个符合条件的值,否则,返回null。

1
2
def value = [1, 3, 5, 7, 9].find{element -> element > 6}
println "Found:${value}"

输出结果为:

Found:7

findAll方法将遍历所有元素,并返回一个符合条件的列表。

1
2
def values = [1, 3, 5, 7, 9].findAll{element -> element > 6}
values.each{println it}

输出结果为:

7
9

collect方法和闭包

collect将遍历某个集合,并使用闭包里的变换方法,将集合中的每个元素转换为一个新值。

collect简单示例:

1
2
def list = [1, 2, 3, 4].collect{element -> return element * element}
println "list:${list}"

输出结果为:

list:[1, 4, 9, 16]

collect高级示例:

1
2
3
4
5
def doubles = {item -> 2 * item}
def map(clos, list){
return list.collect(clos)
}
println "Doubling:${map(doubles, [1, 2, 3, 4])}"

输出结果为:

Doubling:[2, 4, 6, 8]

inject方法和闭包

inject方法可用于遍历集合。

inject简单示例:

1
2
def factorial = [2, 3, 4, 5].inject(1){previous, element -> previous * element}
println "Factorial(5):${factorial}"

输出结果为:

Factorial(5):120

类的实例

无构造器

下面简单介绍Groovy中的类,基本上和Java里的类是一样的。

一个简单的Groovy类

1
2
3
4
5
6
class People{
def name
def age
}
def p = new People(name : '张三', age : 20)
println "People's name is ${p.name} and age is ${p.age}"

这样就定义了一个People类,使用new关键字进行实例化(和Java一样)。

另外说明一点,p.name所示的属性引用方法实际上是通过p.getName()实现的。

自定义构造器

上面介绍的是没有构造器的情况,所以实例化的时候使用的是默认构造器。现在为People增加一个构造器方法,代码如下:

1
2
3
4
5
6
7
8
class People{
def name
def age
def People(name, age){
this.name = name
this.age = age
}
}

此时若想实例化一个People类,只有使用def p = new People('张三', 20)这种方式,如果使用之前的方式来实例化,系统会提示报错,这是因为当类包含一个用户自定义的构造器后,程序并不会自动生成默认的构造器。

类的继承,抽象类和接口类

Groovy中类的继承,抽象类和接口类的概念和Java基本一样,此处不再赘述。

总结

Groovy语言的语法是基于Java的,它包含了许多Python,Ruby和Smalltalk的语言特性。Groovy语言编写的应用程序完全可以使用Java API,它与使用Java编写的框架和组件实现无缝连接。

Groovy2.4开始支持Android开发,但就目前看来,支持的工具非常少,与Scala相比还差得远。因为Groovy是动态语言,在Android上性能可能存在问题。

这篇博客中,作者对Android的备选开发语言里面做了一个调研,这个文档有比较Groovy,Clojure,Sacla和Kotlin的各种优劣势,最终作者倾向于使用kotlin。

在线API doc:http://www.groovy-lang.org/api.html