0%

Groovy之基本语法

这里对Groovy的基本语法进行介绍

abstract.png

数字类型

不同于Java使用基本类型、引用类型进行区分。对于Groovy而言,其一切均是对象。虽然Groovy语法中保留并使用int、short、boolean这些关键字。但并不代表相应的数据类型是基本类型,其使用的依然是Java中相应基本类型的包装类型。示例代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class NumberDemo {   

static void testType1() {
// Groovy 中的数字类型均使用Java中相应包装类型
byte b1 = 1
assert b1 instanceof Byte
Byte b2 = 2
assert b2 instanceof Byte
// 通过as运算符强制类型
def b3 = 3 as byte
assert b3 instanceof Byte
def b4 = 4 as Byte
assert b3 instanceof Byte

short s1 = 1
assert s1 instanceof Short
Short s2 = 2
assert s2 instanceof Short

int i1 = 1
assert i1 instanceof Integer
Integer i2 = 2
assert i2 instanceof Integer

long l1 = 1
assert l1 instanceof Long
Long l2 = 2
assert l2 instanceof Long

float f1 = 1
assert f1 instanceof Float
Float f2 = 2
assert f2 instanceof Float

double d1 = 1
assert d1 instanceof Double
Double d2 = 2
assert d2 instanceof Double

// 类似地对于布尔类型, Groovy boolean 同样使用Java Boolean进行包装
boolean b6 = true
assert b6 instanceof Boolean
Boolean b7 = false
assert b7 instanceof Boolean
}
}

类似地,可以通过在数字后面添加相应的后缀来表示数字的类型。具体地如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class NumberDemo {   
static void testType2() {
// 整型默认类型
def num1 = 15
assert num1 instanceof Integer

def num2 = 15l
def num3 = 15L
assert num2 instanceof Long
assert num3 instanceof Long

// Integer.MAX_VALUE + 1
def num4 = 2147483648
// Groovy会自动选择合适的整数类型
assert num4 instanceof Long

// 整数添加g/G后缀,使用Java BigInteger类型
def num5 = 15g
def num6 = 15G
assert num5 instanceof BigInteger
assert num6 instanceof BigInteger

// 对于浮点数, Groovy默认使用BigDecimal类型
def num7 = 7.7
def num8 = 8.8g
def num9 = 9.9G
assert num7 instanceof BigDecimal
assert num8 instanceof BigDecimal
assert num9 instanceof BigDecimal

def num10 = 5.55f
def num11 = 6.66F
assert num10 instanceof Float
assert num11 instanceof Float

def num12 = 7.77d
def num13 = 8.88D
assert num12 instanceof Double
assert num13 instanceof Double
}
}

前面提到既然数字是对象类型而不是基本类型的,那自然可以直接调用方法。这里就Groovy增强的方法进行实践,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class NumberDemo {   
static void testMethod() {
def msg1 = ""
def num1 = 4
// 对闭包执行指定次数
num1.times { msg1 += "A" }
assert msg1 == "AAAA"
def msg2 = ""
num1.times { e -> msg2 += "B$e" }
assert msg2 == "B0B1B2B3"

def msg3 = ""
// 从3到5依次执行闭包
3.upto(5) { num -> msg3 += num }
assert msg3 == "345"

def msg4 = ""
// 从5到-2依次执行闭包
5.downto(-2) { num -> msg4 += num }
assert msg4 == "543210-1-2"

def msg5 = ""
// 从0.1按0.2的步长增长到1.1, 但不包括1.1
0.1.step(1.1, 0.2) { num -> msg5 += "H$num, " }
assert msg5 == "H0.1, H0.3, H0.5, H0.7, H0.9, "
}
}

字符串

Groovy中支持多种形式的字符串定义方式

单引号

直接利用单引号定义字符串,则其实际上是Java String类的实例

1
2
3
4
5
6
7
8
9
10
11
12
class StringDemo {
static void main(args) {
def x = 1
/*********************** 单引号 ***********************/
def str1 = 'str 1: $x'
// 单引号字符串 是Java String的实例
assert str1 instanceof String
println("-------------------")
// 由于不支持插值, 故会输出 str 1: $x
println(str1)
}
}

测试结果,如下所示

figure 1.jpeg

双引号

在Groovy中使用双引号定义字符串,则是有特殊含义的。即支持使用占位符${}进行插值,其中占位符的花括号{}在不引起歧义的前提下可以省略。具体地,字符串中如果不含占位符, 则其是Java String的实例;反正,则是Groovy GString的实例。示例代码如下所示,可以看到str2b中的x已经被替换为1了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class StringDemo {
static void main(args) {
def x = 1
/*********************** 双引号 ***********************/
def str2a = "str 2a: Hello"
// 双引号字符串 中如果不含占位符, 则其是Java String的实例
assert str2a instanceof String
assert str2a == 'str 2a: Hello'

def str2b = "str 2b: $x"
// 双引号字符串 中如果含占位符, 则其是Groovy GString的实例
assert str2b instanceof GString
assert str2b == 'str 2b: 1'
}
}

三重单引号

Groovy还支持通过三重单引号定义存在多行的字符串。由于其只是一个普通的Java String实例,故不支持通过占位符进行插值。由于在定义多行字符串过程中,字符串的任何缩进均会被视为有效的空格字符。故str3a变量中的字符串都需要顶格写。如果执意进行缩进,那么最后应该通过stripIndent方法移除指定数量的缩进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class StringDemo {
static void main(args) {
def x = 1
/********************** 三重单引号 *********************/
def str3a = '''str3a
line 1
$x
line 2
line 3'''
// 三重单引号字符串 是Java String的实例, 不支持插值
assert str3a instanceof String
println("-------------------")
println(str3a)

// 使用 续航符\ 避免第一行出现换行符
def str3b = '''\
str3b
hello'''
println("-------------------")
// 通过stripIndent方法移除指定数量的缩进
println( str3b.stripIndent(12) )
}
}

测试结果,如下所示

figure 2.jpeg

三重双引号

如果期望对多行字符串支持占位符进行插值,则可进一步通过三重双引号进行定义。类似地,字符串中如果不含占位符, 则其是Java String的实例;反正,则是Groovy GString的实例。示例代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class StringDemo {
static void main(args) {
def x = 1
/********************** 三重双引号 *********************/
def str4a = """str4a
line 1
line 2
line 3"""
// 如果不含占位符, 则其是Java String的实例
assert str3a instanceof String
println("-------------------")
println(str4a)

def str4b = """str4b
line 1
$x
line 3"""
// 如果含占位符, 则其是Groovy GString的实例
assert str4b instanceof GString
println("-------------------")
println(str4b)
}
}

测试结果,如下所示

figure 3.jpeg

斜杠

字符串中如果存在一些特殊字符时,需要使用大量的反斜杠进行转义。为此Groovy提供一种通过斜杠定义字符串的方法。类似地,字符串中如果不含占位符, 则其是Java String的实例;反正,则是Groovy GString的实例。示例代码如下所示。显然此种方式十分适合用来定义、表示正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class StringDemo {
static void main(args) {
def x = 1
// 斜杠表示字符串 Hello"12\World ,无需通过反斜杠进行转义
def str5a = /Hello"12\World/
// 如果不含占位符, 则其是Java String的实例
assert str5a instanceof String
// 可以看到如果使用双引号表示字符串时, 需要使用大量的反斜杠进行转义
assert str5a == "Hello\"12\\World"

def str5b = /a${x}c/
// 如果含占位符, 则其是Groovy GString的实例
assert str5b instanceof GString
assert str5b == 'a1c'
}
}

字符类型

这里对字符类型的字面量作一些补充说明,Groovy中没有专门用来表示单个字符的方式。故可以将只包含单个字符的字符串转换为字符。具体地转换方式如下述代码所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class StringDemo {
static void main(args) {
// 此处实际上通过字符串强制转换为单个字符
char c1 = "A"
// 类似地对于字符类型, Groovy char 同样使用Java Character进行包装
assert c1 instanceof Character

// 此处实际上通过字符串强制转换为单个字符
Character c2 = "B"
assert c2 instanceof Character

// 通过as运算符强制类型
def c3 = "C" as char
assert c3 instanceof Character

// 通过toCharacter方法转换为字符
def c4 = "D".toCharacter()
assert c4 instanceof Character

// 强制类型转换
def c5 = (char)'E'
assert c5 instanceof Character
}
}

表达式真值规则

对于Java而言,其要求表达式的结果必须是布尔类型才可以判断真/假。而在Groovy中表达式真值判定的限制被进一步放宽,其可以对任何一个表达式进行真值判定。下面就Groovy中的表达式的真值规则进行介绍

布尔值可以直接 作为 真值结果

1
2
3
4
// 布尔值true 表示 真
assert true
// 布尔值false 表示 假
assert !false

布尔表达式计算的布尔值结果 作为 真值结果

1
2
3
// 布尔表达式直接计算布尔值结果 作为 真/假
assert 2>1
assert !(2<1)

对于数组、集合而言,非空为真、空为假

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def list1 = ["a", 23]
def list2 = []
// 非空集合 表示 真
assert list1
// 空集合 表示 假
assert !list2

// 非空集合 表示 真
assert ["Ok":200, "Error": 404]
// 空集合 表示 假
def map1 = [:]
assert !map1

def array1 = [211,985] as int[]
def array2 = [] as int[]
// 非空数组 表示 真
assert array1
// 空数组 表示 假
assert !array2

对于字符串而言,非空为真、空为假

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 非空字符 表示 真
assert 'A'
// 空字符 表示 假
assert !''

def str1 = "ABC"
def str2 = ""
// 非空字符串 表示 真
assert str1
// 空字符 表示 假
assert !str2

def str3 = "$str1"
def str4 = "$str2"
// 非空字符串 表示 真
assert str3
// 空字符 表示 假
assert !str4

对于数字而言,非零为真、零为假

1
2
3
4
5
6
7
8
9
// 非零 表示 真
assert 985
assert 3.14f
assert 3.14g
// 零 表示 假
assert !0
assert !0g
assert !0.0f
assert !0.0d

对于引用而言,非NULL为真、NULL为假

需要注意的是对于指向空集合的引用而言,即使非NULL,但根据集合的真值判定规则,空集合为假

1
2
3
4
5
6
7
8
9
10
11
12
def obj = new Object()
// 引用非null表示真
assert obj

def map2 = null
// 空指针null 表示 假
assert !map2

def map3 = new HashMap()
// 虽然引用map3不为null, 但其类型为集合
// 根据集合的真假判定规则, 空集合同样为假
assert !map3

对于正则匹配而言,匹配成功为真、匹配失败为假

1
2
3
4
// 正则匹配成功 表示 真
assert "Aaron" =~ /Aar/
// 正则匹配失败 表示 真
assert !("Aaron" =~ /Bob/)

异常

对于异常机制而言,Groovy与Java一样不仅支持传统的try/catch/finally语句,同样还支持资源自动管理ARM机制,即TWR语法。不同之处在于,Groovy方法无论是抛出CheckedException受查异常,还是抛出RuntimeException运行时异常。方法签名处的异常抛出声明均是可选的。示例如下所示,可以看出总体上与Java保持了一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* Groovy 异常 示例
*/
class ExceptionDemo {

static void main(String[] args) {
def result1 = test1("Java 8 In Action")
assert result1 =="JAVA 8 IN ACTION"

/*********************** 支持传统的try/catch语句 ***********************/
try {
test1("Groovy In Action")
} catch (e) { // 如果期望捕获所有类型的异常, 异常类型可省略
assert e instanceof FileNotFoundException
assert e.getMessage() == "文件不存在异常"
}

/*********************** 支持传统的try/catch/finally语句 ***********************/
def result2 = -1
try{
test2(null)
} catch (NullPointerException | ArithmeticException e) {
// 支持同时捕获多种类型异常
assert e.getMessage() == "num不能为null" || "num不能为负数"
} finally {
result2 = 996
}
assert result2 == 996

/*********************** 支持资源自动管理ARM机制, 即TWR语法 ***********************/
try ( FileReader file = new FileReader("src/main/resources/ReadMe.txt") ) {
file.each {line -> println line}
}catch(e) {
println ("Happen Exception: ${e.getMessage()}")
}
}

/**
* Groovy 方法抛出受查异常
* Note: Groovy直接抛出CheckedException受查异常时, 在方法签名处的抛出异常声明则是可选的
* @param fileName
* @return
*/
// 推荐在方法签名处显式添加声明: static def test1 (String fileName) throws FileNotFoundException
static def test1 (String fileName) {
if( fileName == "Groovy In Action" ) {
throw new FileNotFoundException("文件不存在异常")
}
return fileName.toUpperCase()
}

/**
* Groovy 方法抛出运行时异常
* @param num
* @return
*/
static int test2 (def num) {
if( num == null ) {
// 类似地, 直接抛出RuntimeException
throw new NullPointerException("num不能为null")
} else if( num<0 ) {
// 类似地, 直接抛出RuntimeException
throw new ArithmeticException("num不能为负数")
}
return num
}
}

控制结构

if语句

Groovy在if语句上与Java并无二致,只不过在条件表达式上更加丰富。可以使用任何的表达式,并应用上文所述的真值规则判定表达式的结果。示例代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class StatementDemo {  
static void testIf() {
def x
if (true) {
x = 1
} else {
x = 2
}
assert x == 1

if (false) {
x = 1
} else {
x = 2
}
assert x == 2

// 条件中 null 被处理为 false
if (null) {
x = 1
} else {
x = 2
}
assert x == 2

// 条件中非空字符串 被处理为 true
if ("Hello") {
x = 1
} else {
x = 2
}
assert x == 1

// 支持 if - else if 语句
def foo = 99
def bar
if (foo==1) {
bar = 1
} else if (foo==2) {
bar = 2
} else {
bar = 3
}
assert bar == 3
}
}

while语句

whiile语句方面,同样支持while、do-while两种形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class StatementDemo {   
static void testWhile() {
def bar = 0
while ( bar < 10 ) {
bar++
}
assert bar == 10

// 支持 do - while 语句
def count = 4
def fact = 1
do{
fact *= count
count--
}while (count>1)
assert fact == 24
}
}

for语句

Groovy不仅支持传统for循环、Java经典的for each循环,还针对集合容器提供了for in循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
class StatementDemo {
static testFor() {
/************* 传统 for 循环 *************/
def msg = ""
for(int i=1; i<=5; i++) {
msg += i
}
assert msg == "12345"

// for语句 支持多赋值
def str = ""
for( def (x, y) = ["Hello", 42]; y<45; x++, y++) {
str += "$x $y, "
}
assert str == "Hello 42, Hellp 43, Hellq 44, "

/************* for in 迭代集合容器 *************/
// for in 迭代 List
def list = [1,3,5]
def x = 0
for( e in list ) {
x += e
}
assert x == 9

// for in 迭代 Array
def array = [2, 6, 4] as int[]
def y = 0
for( e in array ) {
y += e
}
assert y == 12

// for in 迭代 Map
def map = ["Aaron":1, "Bob":3, "Tina":2]
def names = ""
def sum = 0
for( e in map ) {
names += e.key + ","
sum += e.value
}
assert names == "Aaron,Bob,Tina,"
assert sum == 6

def sum1 = 0
for ( e in map.values()) {
sum1 += e
}
assert sum1 == 6

// for in 迭代 字符串中的字符
def text = "Hello"
def list2 = []
for ( c in text ) {
list2.add( c )
}
assert list2 == ["H", "e", "l", "l", "o"]

// for in 迭代 range
def range = 1..3
def result2 = 0
for( e in range ) {
result2 += e
}
assert result2 == 6

/************* 经典 for each 循环 *************/
// for each 迭代 List
def list3 = [1,3,5]
def result = 0
// 使用for each循环需要指定e变量的类型
// 这里可以使用int, 也可以直接使用def
for(def e : list3) {
result += e
}
assert result == 9
}
}

switch语句

Groovy支持传统的switch语句.需要注意的是,当匹配到某个case时如若未遇到break,则其会一直执行下去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class StatementDemo {
static testSwitch1() {
def result = ""
def num = 3
switch (num) {
case 0: result += "A"
case 1: {
result += "B"
break
}
case 2: result += "C"
case 3: result += "D"
case 4: {
result += "E"
break
}
case 5: result += "F"
default: result += "Z"
}
assert result == "DE"
}
}

特别地,Groovy中的Switch语句还支持使用分类器,用来判断某个值是否属于某个分类。示例如下所示。事实上进行分类判断时,本质上是通过调用分类器的isCase方法实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class StatementDemo {
static testSwitch2() {
Closure closure1 = { num ->
def result = "Z"
switch (num) {
case -1 : result = "A"; break
// 通过Range的isCase方法, 相当于 (1..<2).isCase(num)
case 1..<2 : result = "B"; break
// 调用Range的isCase方法, 相当于 (1..<2).isCase(num)
case 2..4 : result = "C"; break
// 调用闭包的isCase方法, 相当于 {it%5==0}.isCase(num)
case {it%5==0} : result = "D"; break
// 调用列表的isCase方法, 相当于 [11,31].isCase(num)
case [11,31] : result = "E"; break
// 调用Integer的isCase方法, 相当于 Integer.isCase(num)
case Integer: result = "F"; break

}
return result
}

assert closure1(-1) == "A"
assert closure1(1) == "B"
assert closure1(2) == "C"
assert closure1(5) == "D"
assert closure1(10) == "D"
assert closure1(31) == "E"
assert closure1(996) == "F"
}
}

参考文献

  1. Groovy In Action · 2nd Edition Dierk König、Guillaume Laforge著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝