1. 介紹
本篇內(nèi)容為Groovy學(xué)習(xí)第30篇內(nèi)容,從本篇開始將會(huì)學(xué)習(xí)Groovy語(yǔ)法中的控制結(jié)構(gòu)
例如:if/else,switch/case ,try/cathc 等等。
2. 控制結(jié)構(gòu)
控制結(jié)構(gòu)是指以某種順序執(zhí)行的一系列動(dòng)作,用于解決某個(gè)問(wèn)題。最基本的控制結(jié)構(gòu)分為:順序,選擇,循環(huán)。
2.1 條件控制 structures
Groovy中的條件控制語(yǔ)句和java中的是一樣的,也是if-else 和switch - case
2.1.1 if-else語(yǔ)句
Groovy支持來(lái)自Java的常用if - else語(yǔ)法。實(shí)現(xiàn)示例如下:
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 語(yǔ)句
Groovy中的switch語(yǔ)句向后兼容Java代碼;因此,您可以在多個(gè)匹配的情況下共享相同的代碼。
不過(guò)有一個(gè)區(qū)別是,Groovy switch語(yǔ)句可以處理任何類型的switch值,并且可以執(zhí)行不同類型的匹配。
示例如下:
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的值是類的實(shí)例,則類用例值匹配。
- 如果switch值的toString()表示與正則表達(dá)式匹配,則正則表達(dá)式大小寫值匹配。
- 如果switch值包含在集合中,則集合用例值匹配。這也包括范圍(因?yàn)樗鼈兪橇斜?。
- 如果調(diào)用閉包返回一個(gè)根據(jù)Groovy truth為true的結(jié)果,閉包大小寫值就匹配。
如果以上任何一個(gè)都沒有被使用,那么如果case值等于開關(guān)值,則case值匹配。
當(dāng)使用閉包大小寫值時(shí),默認(rèn)的it參數(shù)實(shí)際上是switch值(在我們的示例中是變量x)。
Groovy還支持如下示例所示的switch表達(dá)式:
def partner = switch(person) {
case 'Romeo' -> 'Juliet'
case 'Adam' -> 'Eve'
case 'Antony' -> 'Cleopatra'
case 'Bonnie' -> 'Clyde'
}
2.2 循環(huán)結(jié)構(gòu) Looping structures
簡(jiǎn)單介紹幾種常見的,也是必須掌握的循環(huán)結(jié)構(gòu),例如for,while,do while結(jié)構(gòu)寫法。
2.2.1 for循環(huán)語(yǔ)句
Groovy支持標(biāo)準(zhǔn)的Java 或 C 語(yǔ)言的for循環(huán):
String message = '' //創(chuàng)建一個(gè)變量
//通過(guò)for循環(huán) 循環(huán)4次進(jìn)行賦值操作。
for (int i = 0; i < 4; i++) {
message += 'zinyan '
}
prinlnt message //輸出:zinyan zinyan zinyan zinyan
也支持使用逗號(hào)分隔表達(dá)式的更復(fù)雜的Java經(jīng)典for循環(huán)形式。例子:
def facts = []
def count = 5
for (int fact = 1, i = 1; i <= count; i++, fact *= i) {
facts << fact //<< 表示給集合添加對(duì)象哦
}
println facts //輸出:[1, 2, 6, 24, 120]
上一篇介紹的多賦值操作與for語(yǔ)句也可以結(jié)合使用。29. Groovy 語(yǔ)法-變量定義與多重賦值 (zinyan.com)
PS:多賦值操作是從Groovy 1.6 版本開始支持的。如果你的編譯器報(bào)錯(cuò),那么說(shuō)明你的sdk版本太老了。
// 普通的進(jìn)行一個(gè)多賦值操作。 不懂的可以看第29篇的內(nèi)容。
def (String x, int y) = ['foo', 42]
// 多賦值操作和for循環(huán)結(jié)合使用:
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循環(huán)要簡(jiǎn)單得多,可用于任何類型的數(shù)組、集合、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還支持使用冒號(hào)的Java冒號(hào)變體:for (char c: text) {}的循環(huán)結(jié)構(gòu)。
2.2.2 while 循環(huán)語(yǔ)句
Groovy像Java一樣支持常見的while{…}循環(huán):
def x = 0
def y = 5
//創(chuàng)建一個(gè)while循環(huán),每次循環(huán)會(huì)后y進(jìn)行減少,直到y(tǒng)小于等于0的時(shí)候,結(jié)束循環(huán)
while ( y-- > 0 ) {
x++
}
println x //輸出5
要注意,while的循環(huán)方法如果創(chuàng)建的條件不對(duì),是容易出現(xiàn)無(wú)限循環(huán)的,也就是死循環(huán)。
因?yàn)閣hile的條件一直為true的話,while就不會(huì)退出了。
2.2.3 do..while 循環(huán)語(yǔ)句
和while一樣,Groovy中的do...while 循環(huán)語(yǔ)句和java中的實(shí)現(xiàn)是一樣的。
def count = 5
def fact = 1
do {
fact *= count--
} while(count > 1)
println face //輸出 :120
3 異常-Exception
異常處理,其實(shí)也是控制結(jié)構(gòu)的一種。通過(guò)異常進(jìn)行強(qiáng)制結(jié)束程序的執(zhí)行順序。
Groovy沒有特殊的異常處理機(jī)制,它的Exception是和java的處理是一樣的。
3.1 try.. catch、finally語(yǔ)句
可以指定一組完整的try-catch-finally、try-catch或try-finally塊。
PS:如果完全不了解try塊的話,建議查詢java中異常捕獲機(jī)制try結(jié)構(gòu)的使用。
簡(jiǎn)單理解try語(yǔ)句就是,當(dāng)某段代碼出現(xiàn)了異常的時(shí)候,為了避免程序崩潰。我們主動(dòng)進(jìn)行防護(hù)。
就是使用try語(yǔ)句來(lái)實(shí)現(xiàn)的。catch只是出現(xiàn)了異常后我們需要程序執(zhí)行的內(nèi)容。
如果沒有異常,將會(huì)自動(dòng)按照順序執(zhí)行代碼(ps:不會(huì)執(zhí)行cath里面的代碼)。
簡(jiǎn)單的示例如下:
try {
'zinyan'.toLong() //把一個(gè)字符串轉(zhuǎn)long也會(huì)出現(xiàn)數(shù)據(jù)類型轉(zhuǎn)換異常
assert false // assert斷言必須執(zhí)行true,如果是false就會(huì)出現(xiàn)異常
} catch ( e ) {
assert e in NumberFormatException
}
如果想代碼不管是否出現(xiàn)異常,都進(jìn)行執(zhí)行。并根據(jù)異?;蚍钱惓5慕Y(jié)果進(jìn)行計(jì)算并執(zhí)行。那么我們可以使用finally子句
因?yàn)闊o(wú)論try子句中的代碼是否拋出異常,finally子句中的代碼都將始終執(zhí)行。
示例如下:
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以來(lái)),我們能夠定義幾個(gè)要被捕獲并由相同捕獲塊處理的異常:
try {
/* ... */
} catch ( IOException | NullPointerException e ) {
/* one block to handle 2 exceptions */
}
3.3 ARM Try 資源
對(duì)于自動(dòng)資源管理(ARM), Groovy通常為Java 7的try-with-resources語(yǔ)句提供更好的替代方案?,F(xiàn)在,遷移到Groovy并仍然希望使用舊風(fēng)格的Java程序員支持這種語(yǔ)法:
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')
將會(huì)輸出以下內(nèi)容:
ToResource closing
FromResource closing
ToResource closing
FromResource closing
4. 強(qiáng)大斷言 Power asserts
與Groovy共享assert關(guān)鍵字的Java不同,后者在Groovy中的行為非常不同。首先,Groovy中的斷言總是獨(dú)立于JVM的-ea標(biāo)志執(zhí)行。這使得它成為單元測(cè)試的首選?!皬?qiáng)大斷言”的概念與Groovy斷言的行為方式直接相關(guān)。
一個(gè)強(qiáng)大斷言被分解為三個(gè)部分:assert [left expression] == [right expression] : (optional message)
斷言的結(jié)果與在Java中得到的結(jié)果非常不同。如果斷言為真,那么什么也不會(huì)發(fā)生。如果斷言為假,那么它提供被斷言表達(dá)式的每個(gè)子表達(dá)式的值的可視化表示。例如:
將會(huì)打印下面的內(nèi)容:
Caught: Assertion failed:
assert 1+1 == 3
| |
2 false
Assertion failed:
assert 1+1 == 3
| |
2 false
at zinyan.run(zinyan.groovy:1)
當(dāng)表達(dá)式更復(fù)雜時(shí),權(quán)力斷言變得非常有趣,就像在下一個(gè)例子中:
def x = 2
def y = 7
def z = 5
def calc = { a,b -> a*b+1 }
assert calc(x,y) == [x,z].sum()
我們執(zhí)行上面的代碼后,將會(huì)輸出:
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)
如果不想要像上面那樣漂亮的打印錯(cuò)誤消息,可以通過(guò)更改斷言的可選消息部分來(lái)回退到自定義錯(cuò)誤消息,就像下面的例子:
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'
將會(huì)輸出以下錯(cuò)誤內(nèi)容:
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. 標(biāo)簽聲明
任何語(yǔ)句都可以與標(biāo)簽相關(guān)聯(lián)。標(biāo)簽不影響代碼的語(yǔ)義,可用于使代碼更容易閱讀,如下例所示:
given:
def x = 1
def y = 2
when:
def z = x+y
then:
assert z == 3
zinyan:
println "zinyan.com"
在上面的示例中,given,when,then ,zinyan都是屬于標(biāo)簽。這些標(biāo)簽,并不會(huì)影響代碼的運(yùn)行結(jié)果和邏輯。
標(biāo)簽并沒有特殊的關(guān)鍵字,標(biāo)簽名稱可以隨意定義。
盡管沒有更改標(biāo)記語(yǔ)句的語(yǔ)義,但可以在break指令中使用標(biāo)簽作為跳轉(zhuǎn)的目標(biāo)。示例如下:
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官方不推薦大家這樣使用標(biāo)簽。因?yàn)槿菀自斐烧`解和歧義。
默認(rèn)情況下標(biāo)簽對(duì)代碼的語(yǔ)義沒有影響,但是它們屬于抽象語(yǔ)法樹(AST),因此AST轉(zhuǎn)換可以使用該信息對(duì)代碼執(zhí)行轉(zhuǎn)換,從而導(dǎo)致不同的語(yǔ)義。這就是Spock框架為簡(jiǎn)化測(cè)試所做的工作。
6. 小結(jié)
本篇內(nèi)容介紹到這里就結(jié)束了,大家重點(diǎn)了解控制結(jié)構(gòu)的相關(guān)寫法和實(shí)現(xiàn)邏輯以及標(biāo)簽的基本聲明方式就可以了。
對(duì)于斷言和特殊的標(biāo)簽使用場(chǎng)景,可以做一個(gè)擴(kuò)展知識(shí)點(diǎn)的學(xué)習(xí)。一般在實(shí)際工作中用到的比較少。
以上內(nèi)容的知識(shí)來(lái)源于Groovy官方文檔:Groovy Language Documentation (groovy-lang.org)的學(xué)習(xí)筆記。