30. Groovy 语法-控制结构:if,switch,for,try等知识

2023-02-23 17:44:10 浏览数 (2)

1. 介绍

本篇内容为Groovy学习第30篇内容,从本篇开始将会学习Groovy语法中的控制结构

例如:if/else,switch/case ,try/cathc 等等。

2. 控制结构

控制结构是指以某种顺序执行的一系列动作,用于解决某个问题。最基本的控制结构分为:顺序,选择,循环。

2.1 条件控制 structures

Groovy中的条件控制语句和java中的是一样的,也是if-elseswitch - case

2.1.1 if-else语句

Groovy支持来自Java的常用if - else语法。实现示例如下:

代码语言:javascript复制
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 嵌套格式:

代码语言:javascript复制
if ( ... ) {
    ...
} else if (...) {
    ...
} else {
    ...
}
2.1.2 switch-case 语句

Groovy中的switch语句向后兼容Java代码;因此,您可以在多个匹配的情况下共享相同的代码。

不过有一个区别是,Groovy switch语句可以处理任何类型的switch值,并且可以执行不同类型的匹配。

示例如下:

代码语言:javascript复制
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表达式:

代码语言:javascript复制
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循环:

代码语言:javascript复制
String message = '' //创建一个变量
//通过for循环 循环4次进行赋值操作。
for (int i = 0; i < 4; i  ) {
    message  = 'zinyan '
}
prinlnt message //输出:zinyan zinyan zinyan zinyan

也支持使用逗号分隔表达式的更复杂的Java经典for循环形式。例子:

代码语言:javascript复制
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版本太老了。

代码语言:javascript复制
// 普通的进行一个多赋值操作。 不懂的可以看第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等。

代码语言:javascript复制
// 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{…}循环:

代码语言:javascript复制
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中的实现是一样的。

代码语言:javascript复制
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-finallytry-catchtry-finally块。

PS:如果完全不了解try块的话,建议查询java中异常捕获机制try结构的使用。

简单理解try语句就是,当某段代码出现了异常的时候,为了避免程序崩溃。我们主动进行防护。

就是使用try语句来实现的。catch只是出现了异常后我们需要程序执行的内容。

如果没有异常,将会自动按照顺序执行代码(ps:不会执行cath里面的代码)。

简单的示例如下:

代码语言:javascript复制
try {
    'zinyan'.toLong()   //把一个字符串转long也会出现数据类型转换异常
    assert false     // assert断言必须执行true,如果是false就会出现异常
} catch ( e ) {
    assert e in NumberFormatException
}

如果想代码不管是否出现异常,都进行执行。并根据异常或非异常的结果进行计算并执行。那么我们可以使用finally子句

因为无论try子句中的代码是否抛出异常,finally子句中的代码都将始终执行。

示例如下:

代码语言:javascript复制
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以来),我们能够定义几个要被捕获并由相同捕获块处理的异常:

代码语言:javascript复制
try {
    /* ... */
} catch ( IOException | NullPointerException e ) {
    /* one block to handle 2 exceptions */
}
3.3 ARM Try 资源

对于自动资源管理(ARM), Groovy通常为Java 7的try-with-resources语句提供更好的替代方案。现在,迁移到Groovy并仍然希望使用旧风格的Java程序员支持这种语法:

代码语言:javascript复制
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')

将会输出以下内容:

代码语言:javascript复制
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中得到的结果非常不同。如果断言为真,那么什么也不会发生。如果断言为假,那么它提供被断言表达式的每个子表达式的值的可视化表示。例如:

代码语言:javascript复制
assert 1 1 == 3

将会打印下面的内容:

代码语言:javascript复制
Caught: Assertion failed: 

assert 1 1 == 3
        |  |
        2  false

Assertion failed: 

assert 1 1 == 3
        |  |
        2  false

	at zinyan.run(zinyan.groovy:1)

当表达式更复杂时,权力断言变得非常有趣,就像在下一个例子中:

代码语言:javascript复制
def x = 2
def y = 7
def z = 5
def calc = { a,b -> a*b 1 }
assert calc(x,y) == [x,z].sum()

我们执行上面的代码后,将会输出:

代码语言:javascript复制
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)

如果不想要像上面那样漂亮的打印错误消息,可以通过更改断言的可选消息部分来回退到自定义错误消息,就像下面的例子:

代码语言:javascript复制
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'

将会输出以下错误内容:

代码语言:javascript复制
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. 标签声明

任何语句都可以与标签相关联。标签不影响代码的语义,可用于使代码更容易阅读,如下例所示:

代码语言:javascript复制
given:
    def x = 1
    def y = 2
when:
    def z = x y
then:
    assert z == 3
zinyan:
  println "zinyan.com"

在上面的示例中,givenwhenthenzinyan都是属于标签。这些标签,并不会影响代码的运行结果和逻辑。

标签并没有特殊的关键字,标签名称可以随意定义。

尽管没有更改标记语句的语义,但可以在break指令中使用标签作为跳转的目标。示例如下:

代码语言:javascript复制
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)的学习笔记。

如果觉得我的总结内容还算清晰,希望能够给我点个赞鼓励一下。谢谢。

0 人点赞