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
}
如果想代碼不管是否出現異常,都進行執行。并根據異?;蚍钱惓5慕Y果進行計算并執行。那么我們可以使用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語句提供更好的替代方案?,F在,遷移到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)的學習筆記。