1. 介绍
本篇内容为Groovy学习第30篇内容,从本篇开始将会学习Groovy语法中的控制结构
例如:if/else,switch/case ,try/cathc 等等。
2. 控制结构
控制结构是指以某种顺序执行的一系列动作,用于解决某个问题。最基本的控制结构分为:顺序,选择,循环。
2.1 条件控制 structures
Groovy中的条件控制语句和java中的是一样的,也是if-else 和switch - case
2.1.1 if-else语句
Groovy支持来自Java的常用if - else语法。实现示例如下:
def x = false
def y = false
if ( !x ) {
x = true
}
println x //输出 true
if ( x ) {
x = false
} else {
y = true
}
println x //输出 false
println y //输出: false
也支持常见的if else if 嵌套格式:
if ( ... ) {
...
} else if (...) {
...
} else {
...
}
2.1.2 switch-case 语句
Groovy中的switch语句向后兼容Java代码;因此,您可以在多个匹配的情况下共享相同的代码。
不过有一个区别是,Groovy switch语句可以处理任何类型的switch值,并且可以执行不同类型的匹配。
示例如下:
def x = 1.23
def result = ""
switch (x) {
case "foo":
result = "found foo"
// lets fall through
case "bar":
result += "bar"
case [4, 5, 6, 'inList']:
result = "list"
break
case 12..30:
result = "range"
break
case Integer:
result = "integer"
break
case Number:
result = "number"
break
case ~/fo*/: // toString() representation of x matches the pattern?
result = "foo regex"
break
case { it < 0 }: // or { x < 0 }
result = "negative"
break
default:
result = "default"
}
println result //输出: number
Switch支持以下几种比较:
- 如果switch的值是类的实例,则类用例值匹配。
- 如果switch值的toString()表示与正则表达式匹配,则正则表达式大小写值匹配。
- 如果switch值包含在集合中,则集合用例值匹配。这也包括范围(因为它们是列表)。
- 如果调用闭包返回一个根据Groovy truth为true的结果,闭包大小写值就匹配。
如果以上任何一个都没有被使用,那么如果case值等于开关值,则case值匹配。
当使用闭包大小写值时,默认的it参数实际上是switch值(在我们的示例中是变量x)。
Groovy还支持如下示例所示的switch表达式:
def partner = switch(person) {
case 'Romeo' -> 'Juliet'
case 'Adam' -> 'Eve'
case 'Antony' -> 'Cleopatra'
case 'Bonnie' -> 'Clyde'
}
2.2 循环结构 Looping structures
简单介绍几种常见的,也是必须掌握的循环结构,例如for,while,do while结构写法。
2.2.1 for循环语句
Groovy支持标准的Java 或 C 语言的for循环:
String message = '' //创建一个变量
//通过for循环 循环4次进行赋值操作。
for (int i = 0; i < 4; i++) {
message += 'zinyan '
}
prinlnt message //输出:zinyan zinyan zinyan zinyan
也支持使用逗号分隔表达式的更复杂的Java经典for循环形式。例子:
def facts = []
def count = 5
for (int fact = 1, i = 1; i <= count; i++, fact *= i) {
facts << fact //<< 表示给集合添加对象哦
}
println facts //输出:[1, 2, 6, 24, 120]
上一篇介绍的多赋值操作与for语句也可以结合使用。29. Groovy 语法-变量定义与多重赋值 (zinyan.com)
PS:多赋值操作是从Groovy 1.6 版本开始支持的。如果你的编译器报错,那么说明你的sdk版本太老了。
// 普通的进行一个多赋值操作。 不懂的可以看第29篇的内容。
def (String x, int y) = ['foo', 42]
// 多赋值操作和for循环结合使用:
def baNums = []
for (def (String u, int v) = ['bar', 42]; v < 45; u++, v++) {
baNums << "$u $v"
}
println baNums //输出:['bar 42', 'bas 43', 'bat 44']
Groovy中的for循环要简单得多,可用于任何类型的数组、集合、Map等。
// iterate over a range
def x = 0
for ( i in 0..9 ) {
x += i
}
assert x == 45
// iterate over a list
x = 0
for ( i in [0, 1, 2, 3, 4] ) {
x += i
}
assert x == 10
// iterate over an array
def array = (0..4).toArray()
x = 0
for ( i in array ) {
x += i
}
assert x == 10
// iterate over a map
def map = ['abc':1, 'def':2, 'xyz':3]
x = 0
for ( e in map ) {
x += e.value
}
assert x == 6
// iterate over values in a map
x = 0
for ( v in map.values() ) {
x += v
}
assert x == 6
// iterate over the characters in a string
def text = "abc"
def list = []
for (c in text) {
list.add(c)
}
assert list == ["a", "b", "c"]
Groovy还支持使用冒号的Java冒号变体:for (char c: text) {}的循环结构。
2.2.2 while 循环语句
Groovy像Java一样支持常见的while{…}循环:
def x = 0
def y = 5
//创建一个while循环,每次循环会后y进行减少,直到y小于等于0的时候,结束循环
while ( y-- > 0 ) {
x++
}
println x //输出5
要注意,while的循环方法如果创建的条件不对,是容易出现无限循环的,也就是死循环。
因为while的条件一直为true的话,while就不会退出了。
2.2.3 do..while 循环语句
和while一样,Groovy中的do...while 循环语句和java中的实现是一样的。
def count = 5
def fact = 1
do {
fact *= count--
} while(count > 1)
println face //输出 :120
3 异常-Exception
异常处理,其实也是控制结构的一种。通过异常进行强制结束程序的执行顺序。
Groovy没有特殊的异常处理机制,它的Exception是和java的处理是一样的。
3.1 try.. catch、finally语句
可以指定一组完整的try-catch-finally、try-catch或try-finally块。
PS:如果完全不了解try块的话,建议查询java中异常捕获机制try结构的使用。
简单理解try语句就是,当某段代码出现了异常的时候,为了避免程序崩溃。我们主动进行防护。
就是使用try语句来实现的。catch只是出现了异常后我们需要程序执行的内容。
如果没有异常,将会自动按照顺序执行代码(ps:不会执行cath里面的代码)。
简单的示例如下:
try {
'zinyan'.toLong() //把一个字符串转long也会出现数据类型转换异常
assert false // assert断言必须执行true,如果是false就会出现异常
} catch ( e ) {
assert e in NumberFormatException
}
如果想代码不管是否出现异常,都进行执行。并根据异常或非异常的结果进行计算并执行。那么我们可以使用finally子句
因为无论try子句中的代码是否抛出异常,finally子句中的代码都将始终执行。
示例如下:
def z
try {
def i = 7, j = 0
try {
def k = i / j
assert false //never reached due to Exception in previous line
} finally {
z = 'reached here' //always executed even if Exception thrown
}
} catch ( e ) {
assert e in ArithmeticException
assert z == 'reached here'
}
3.2 多重catch子句
使用多捕获块(自Groovy 2.0以来),我们能够定义几个要被捕获并由相同捕获块处理的异常:
try {
/* ... */
} catch ( IOException | NullPointerException e ) {
/* one block to handle 2 exceptions */
}
3.3 ARM Try 资源
对于自动资源管理(ARM), Groovy通常为Java 7的try-with-resources语句提供更好的替代方案。现在,迁移到Groovy并仍然希望使用旧风格的Java程序员支持这种语法:
class FromResource extends ByteArrayInputStream {
@Override
void close() throws IOException {
super.close()
println "FromResource closing"
}
FromResource(String input) {
super(input.toLowerCase().bytes)
}
}
class ToResource extends ByteArrayOutputStream {
@Override
void close() throws IOException {
super.close()
println "ToResource closing"
}
}
def wrestle(s) {
try (
FromResource from = new FromResource(s)
ToResource to = new ToResource()
) {
to << from
return to.toString()
}
}
def wrestle2(s) {
FromResource from = new FromResource(s)
try (from; ToResource to = new ToResource()) { // Enhanced try-with-resources in Java 9+
to << from
return to.toString()
}
}
assert wrestle("ARM was here!").contains('arm')
assert wrestle2("ARM was here!").contains('arm')
将会输出以下内容:
ToResource closing
FromResource closing
ToResource closing
FromResource closing
4. 强大断言 Power asserts
与Groovy共享assert关键字的Java不同,后者在Groovy中的行为非常不同。首先,Groovy中的断言总是独立于JVM的-ea标志执行。这使得它成为单元测试的首选。“强大断言”的概念与Groovy断言的行为方式直接相关。
一个强大断言被分解为三个部分:assert [left expression] == [right expression] : (optional message)
断言的结果与在Java中得到的结果非常不同。如果断言为真,那么什么也不会发生。如果断言为假,那么它提供被断言表达式的每个子表达式的值的可视化表示。例如:
将会打印下面的内容:
Caught: Assertion failed:
assert 1+1 == 3
| |
2 false
Assertion failed:
assert 1+1 == 3
| |
2 false
at zinyan.run(zinyan.groovy:1)
当表达式更复杂时,权力断言变得非常有趣,就像在下一个例子中:
def x = 2
def y = 7
def z = 5
def calc = { a,b -> a*b+1 }
assert calc(x,y) == [x,z].sum()
我们执行上面的代码后,将会输出:
Caught: Assertion failed:
assert calc(x,y) == [x,z].sum()
| | | | | | |
15 2 7 | 2 5 7
false
Assertion failed:
assert calc(x,y) == [x,z].sum()
| | | | | | |
15 2 7 | 2 5 7
false
at zinyan.run(zinyan.groovy:5)
如果不想要像上面那样漂亮的打印错误消息,可以通过更改断言的可选消息部分来回退到自定义错误消息,就像下面的例子:
def x = 2
def y = 7
def z = 5
def calc = { a,b -> a*b+1 }
assert calc(x,y) == z*z : 'Incorrect computation result'
将会输出以下错误内容:
Caught: java.lang.AssertionError: Incorrect computation result. Expression: (calc.call(x, y) == (z * z)). Values: z = 5, z = 5
java.lang.AssertionError: Incorrect computation result. Expression: (calc.call(x, y) == (z * z)). Values: z = 5, z = 5
at zinyan.run(zinyan.groovy:5)
5. 标签声明
任何语句都可以与标签相关联。标签不影响代码的语义,可用于使代码更容易阅读,如下例所示:
given:
def x = 1
def y = 2
when:
def z = x+y
then:
assert z == 3
zinyan:
println "zinyan.com"
在上面的示例中,given,when,then ,zinyan都是属于标签。这些标签,并不会影响代码的运行结果和逻辑。
标签并没有特殊的关键字,标签名称可以随意定义。
尽管没有更改标记语句的语义,但可以在break指令中使用标签作为跳转的目标。示例如下:
for (int i=0;i<10;i++) {
for (int j=0;j<i;j++) {
println "j=$j"
if (j == 5) {
break exit
}
}
exit: println "i=$i"
}
PS:虽然支持这种写法,但是Groovy官方不推荐大家这样使用标签。因为容易造成误解和歧义。
默认情况下标签对代码的语义没有影响,但是它们属于抽象语法树(AST),因此AST转换可以使用该信息对代码执行转换,从而导致不同的语义。这就是Spock框架为简化测试所做的工作。
6. 小结
本篇内容介绍到这里就结束了,大家重点了解控制结构的相关写法和实现逻辑以及标签的基本声明方式就可以了。
对于断言和特殊的标签使用场景,可以做一个扩展知识点的学习。一般在实际工作中用到的比较少。
以上内容的知识来源于Groovy官方文档:Groovy Language Documentation (groovy-lang.org)的学习笔记。