Groovy之基本語法
這里對Groovy的基本語法進(jìn)行介紹

數(shù)字類型
不同于Java使用基本類型、引用類型進(jìn)行區(qū)分。對于Groovy而言,其一切均是對象。雖然Groovy語法中保留并使用int、short、boolean這些關(guān)鍵字。但并不代表相應(yīng)的數(shù)據(jù)類型是基本類型,其使用的依然是Java中相應(yīng)基本類型的包裝類型。示例代碼如下所示
class?NumberDemo?{???
????static?void?testType1()?{
????????//?Groovy?中的數(shù)字類型均使用Java中相應(yīng)包裝類型
????????byte?b1?=?1
????????assert?b1?instanceof?Byte
????????Byte?b2?=?2
????????assert?b2?instanceof?Byte
????????//?通過as運(yùn)算符強(qiáng)制類型
????????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進(jìn)行包裝
????????boolean?b6?=?true
????????assert?b6?instanceof?Boolean
????????Boolean?b7?=?false
????????assert?b7?instanceof?Boolean
????}
}
類似地,可以通過在數(shù)字后面添加相應(yīng)的后綴來表示數(shù)字的類型。具體地如下所示
class?NumberDemo?{???
????static?void?testType2()?{
????????//?整型默認(rèn)類型
????????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會自動選擇合適的整數(shù)類型
????????assert?num4?instanceof?Long
????????//?整數(shù)添加g/G后綴,使用Java?BigInteger類型
????????def?num5?=?15g
????????def?num6?=?15G
????????assert?num5?instanceof?BigInteger
????????assert?num6?instanceof?BigInteger
????????//?對于浮點(diǎn)數(shù),?Groovy默認(rèn)使用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
????}
}
前面提到既然數(shù)字是對象類型而不是基本類型的,那自然可以直接調(diào)用方法。這里就Groovy增強(qiáng)的方法進(jìn)行實(shí)踐,如下所示
class?NumberDemo?{???
????static?void?testMethod()?{
????????def?msg1?=?""
????????def?num1?=?4
????????//?對閉包執(zhí)行指定次數(shù)
????????num1.times?{?msg1?+=?"A"?}
????????assert?msg1?==?"AAAA"
????????def?msg2?=?""
????????num1.times?{?e?->?msg2?+=?"B$e"?}
????????assert?msg2?==?"B0B1B2B3"
????????def?msg3?=?""
????????//?從3到5依次執(zhí)行閉包
????????3.upto(5)?{?num?->?msg3?+=?num?}
????????assert?msg3?==?"345"
????????def?msg4?=?""
????????//?從5到-2依次執(zhí)行閉包
????????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中支持多種形式的字符串定義方式
單引號
直接利用單引號定義字符串,則其實(shí)際上是Java String類的實(shí)例
class?StringDemo?{
????static?void?main(args)?{
????????def?x?=?1
????????/***********************?單引號?***********************/
????????def?str1?=?'str?1:?$x'
????????//?單引號字符串?是Java?String的實(shí)例
????????assert?str1?instanceof?String
????????println("-------------------")
????????//?由于不支持插值,?故會輸出?str?1:?$x
????????println(str1)
????}
}
測試結(jié)果,如下所示

雙引號
在Groovy中使用雙引號定義字符串,則是有特殊含義的。即支持使用占位符${}進(jìn)行插值,其中占位符的花括號{}在不引起歧義的前提下可以省略。具體地,字符串中如果不含占位符, 則其是Java String的實(shí)例;反正,則是Groovy GString的實(shí)例。示例代碼如下所示,可以看到str2b中的x已經(jīng)被替換為1了
class?StringDemo?{
????static?void?main(args)?{
????????def?x?=?1
????????/***********************?雙引號?***********************/
????????def?str2a?=?"str?2a:?Hello"
????????//?雙引號字符串?中如果不含占位符,?則其是Java?String的實(shí)例
????????assert?str2a?instanceof?String
????????assert?str2a?==?'str?2a:?Hello'
??????
????????def?str2b?=?"str?2b:?$x"
????????//?雙引號字符串?中如果含占位符,?則其是Groovy?GString的實(shí)例
????????assert?str2b?instanceof?GString
????????assert?str2b?==?'str?2b:?1'
????}
}
三重單引號
Groovy還支持通過三重單引號定義存在多行的字符串。由于其只是一個普通的Java String實(shí)例,故不支持通過占位符進(jìn)行插值。由于在定義多行字符串過程中,字符串的任何縮進(jìn)均會被視為有效的空格字符。故str3a變量中的字符串都需要頂格寫。如果執(zhí)意進(jìn)行縮進(jìn),那么最后應(yīng)該通過stripIndent方法移除指定數(shù)量的縮進(jìn)
class?StringDemo?{
????static?void?main(args)?{
????????def?x?=?1
????????????????/**********************?三重單引號?*********************/
????????def?str3a?=?'''str3a
line?1
$x
line?2
line?3'''
????????//?三重單引號字符串?是Java?String的實(shí)例,?不支持插值
????????assert?str3a?instanceof?String
????????println("-------------------")
????????println(str3a)
????????//?使用?續(xù)航符\?避免第一行出現(xiàn)換行符
????????def?str3b?=?'''\
????????????str3b
????????????hello'''
????????println("-------------------")
????????//?通過stripIndent方法移除指定數(shù)量的縮進(jìn)
????????println(?str3b.stripIndent(12)?)
????}
}
測試結(jié)果,如下所示

三重雙引號
如果期望對多行字符串支持占位符進(jìn)行插值,則可進(jìn)一步通過三重雙引號進(jìn)行定義。類似地,字符串中如果不含占位符, 則其是Java String的實(shí)例;反正,則是Groovy GString的實(shí)例。示例代碼如下所示
class?StringDemo?{
????static?void?main(args)?{
????????def?x?=?1
????????/**********************?三重雙引號?*********************/
????????def?str4a?=?"""str4a
line?1
line?2
line?3"""
????????//?如果不含占位符,?則其是Java?String的實(shí)例
????????assert?str3a?instanceof?String
????????println("-------------------")
????????println(str4a)
????????def?str4b?=?"""str4b
line?1
$x
line?3"""
????????//?如果含占位符,?則其是Groovy?GString的實(shí)例
????????assert?str4b?instanceof?GString
????????println("-------------------")
????????println(str4b)
????}
}
測試結(jié)果,如下所示

斜杠
字符串中如果存在一些特殊字符時(shí),需要使用大量的反斜杠進(jìn)行轉(zhuǎn)義。為此Groovy提供一種通過斜杠定義字符串的方法。類似地,字符串中如果不含占位符, 則其是Java String的實(shí)例;反正,則是Groovy GString的實(shí)例。示例代碼如下所示。顯然此種方式十分適合用來定義、表示正則表達(dá)式
class?StringDemo?{
????static?void?main(args)?{
????????def?x?=?1
????????//?斜杠表示字符串?Hello"12\World?,無需通過反斜杠進(jìn)行轉(zhuǎn)義
????????def?str5a?=?/Hello"12\World/
????????//?如果不含占位符,?則其是Java?String的實(shí)例
????????assert?str5a?instanceof?String
????????//?可以看到如果使用雙引號表示字符串時(shí),?需要使用大量的反斜杠進(jìn)行轉(zhuǎn)義
????????assert?str5a?==?"Hello\"12\\World"
????????def?str5b?=?/a${x}c/
????????//?如果含占位符,?則其是Groovy?GString的實(shí)例
????????assert?str5b?instanceof?GString
????????assert?str5b?==?'a1c'
????}
}
字符類型
這里對字符類型的字面量作一些補(bǔ)充說明,Groovy中沒有專門用來表示單個字符的方式。故可以將只包含單個字符的字符串轉(zhuǎn)換為字符。具體地轉(zhuǎn)換方式如下述代碼所示
class?StringDemo?{
????static?void?main(args)?{
????????//?此處實(shí)際上通過字符串強(qiáng)制轉(zhuǎn)換為單個字符
????????char?c1?=?"A"
????????//?類似地對于字符類型,?Groovy?char?同樣使用Java?Character進(jìn)行包裝
????????assert?c1?instanceof?Character
????????//?此處實(shí)際上通過字符串強(qiáng)制轉(zhuǎn)換為單個字符
????????Character?c2?=?"B"
????????assert?c2?instanceof?Character
????????//?通過as運(yùn)算符強(qiáng)制類型
????????def?c3?=?"C"?as?char????
????????assert?c3?instanceof?Character
????????//?通過toCharacter方法轉(zhuǎn)換為字符
????????def?c4?=?"D".toCharacter()
????????assert?c4?instanceof?Character
????????//?強(qiáng)制類型轉(zhuǎn)換
????????def?c5?=?(char)'E'
????????assert?c5?instanceof?Character
????}
}
表達(dá)式真值規(guī)則
對于Java而言,其要求表達(dá)式的結(jié)果必須是布爾類型才可以判斷真/假。而在Groovy中表達(dá)式真值判定的限制被進(jìn)一步放寬,其可以對任何一個表達(dá)式進(jìn)行真值判定。下面就Groovy中的表達(dá)式的真值規(guī)則進(jìn)行介紹
「布爾值可以直接 作為 真值結(jié)果」
//?布爾值true?表示?真
assert?true
//?布爾值false?表示?假
assert?!false
「布爾表達(dá)式計(jì)算的布爾值結(jié)果 作為 真值結(jié)果」
//?布爾表達(dá)式直接計(jì)算布爾值結(jié)果?作為?真/假
assert?2>1
assert?!(2<1)
「對于數(shù)組、集合而言,非空為真、空為假」
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[]
//?非空數(shù)組?表示?真
assert?array1
//?空數(shù)組?表示?假
assert?!array2
「對于字符串而言,非空為真、空為假」
//?非空字符?表示?真
assert?'A'
//?空字符?表示?假
assert?!''
def?str1?=?"ABC"
def?str2?=?""
//?非空字符串?表示?真
assert?str1
//?空字符?表示?假
assert?!str2
def?str3?=?"$str1"
def?str4?=?"$str2"
//?非空字符串?表示?真
assert?str3
//?空字符?表示?假
assert?!str4
「對于數(shù)字而言,非零為真、零為假」
//?非零?表示?真
assert?985
assert?3.14f
assert?3.14g
//?零?表示?假
assert?!0
assert?!0g
assert?!0.0f
assert?!0.0d
「對于引用而言,非NULL為真、NULL為假」
需要注意的是對于指向空集合的引用而言,即使非NULL,但根據(jù)集合的真值判定規(guī)則,空集合為假
def?obj?=?new?Object()
//?引用非null表示真
assert?obj
def?map2?=?null
//?空指針null?表示?假
assert?!map2
def?map3?=?new?HashMap()
//?雖然引用map3不為null,?但其類型為集合
//?根據(jù)集合的真假判定規(guī)則,?空集合同樣為假
assert?!map3
「對于正則匹配而言,匹配成功為真、匹配失敗為假」
//?正則匹配成功?表示?真
assert?"Aaron"?=~?/Aar/
//?正則匹配失敗?表示?真
assert?!("Aaron"?=~?/Bob/)
異常
對于異常機(jī)制而言,Groovy與Java一樣不僅支持傳統(tǒng)的try/catch/finally語句,同樣還支持資源自動管理ARM機(jī)制,即TWR語法。不同之處在于,Groovy方法無論是拋出CheckedException受查異常,還是拋出RuntimeException運(yùn)行時(shí)異常。方法簽名處的異常拋出聲明均是可選的。示例如下所示,可以看出總體上與Java保持了一致
/**
?*?Groovy?異常?示例
?*/
class?ExceptionDemo?{
????static?void?main(String[]?args)?{
????????def?result1?=?test1("Java?8?In?Action")
????????assert?result1?=="JAVA?8?IN?ACTION"
????????/***********************?支持傳統(tǒng)的try/catch語句?***********************/
????????try?{
????????????test1("Groovy?In?Action")
????????}?catch?(e)?{???//?如果期望捕獲所有類型的異常,?異常類型可省略
????????????assert?e?instanceof?FileNotFoundException
????????????assert?e.getMessage()?==?"文件不存在異常"
????????}
????????/***********************?支持傳統(tǒng)的try/catch/finally語句?***********************/
????????def?result2?=?-1
????????try{
????????????test2(null)
????????}?catch?(NullPointerException?|?ArithmeticException?e)?{
????????????//?支持同時(shí)捕獲多種類型異常
????????????assert?e.getMessage()?==?"num不能為null"?||?"num不能為負(fù)數(shù)"
????????}?finally?{
????????????result2?=?996
????????}
????????assert?result2?==?996
????????/***********************?支持資源自動管理ARM機(jī)制,?即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受查異常時(shí),?在方法簽名處的拋出異常聲明則是可選的
?????*?@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?方法拋出運(yùn)行時(shí)異常
?????*?@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不能為負(fù)數(shù)")
????????}
????????return?num
????}
}
控制結(jié)構(gòu)
if語句
Groovy在if語句上與Java并無二致,只不過在條件表達(dá)式上更加豐富。可以使用任何的表達(dá)式,并應(yīng)用上文所述的真值規(guī)則判定表達(dá)式的結(jié)果。示例代碼如下所示
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兩種形式
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不僅支持傳統(tǒng)for循環(huán)、Java經(jīng)典的for each循環(huán),還針對集合容器提供了for in循環(huán)
class?StatementDemo?{
????static?testFor()?{
????????/*************?傳統(tǒng)?for?循環(huán)?*************/
????????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
????????/*************?經(jīng)典?for?each?循環(huán)?*************/
????????//?for?each?迭代?List
????????def?list3?=?[1,3,5]
????????def?result?=?0
????????//?使用for?each循環(huán)需要指定e變量的類型
????????//?這里可以使用int,?也可以直接使用def
????????for(def?e?:?list3)?{
????????????result?+=?e
????????}
????????assert?result?==?9
????}
}
switch語句
Groovy支持傳統(tǒng)的switch語句.需要注意的是,當(dāng)匹配到某個case時(shí)如若未遇到break,則其會一直執(zhí)行下去
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語句還支持使用分類器,用來判斷某個值是否屬于某個分類。示例如下所示。事實(shí)上進(jìn)行分類判斷時(shí),本質(zhì)上是通過調(diào)用分類器的isCase方法實(shí)現(xiàn)的
class?StatementDemo?{
????static?testSwitch2()?{
????????Closure?closure1?=?{?num?->
????????????def?result?=?"Z"
????????????switch?(num)?{
????????????????case?-1?:?result?=?"A";?break
????????????????//?通過Range的isCase方法,?相當(dāng)于?(1..<2).isCase(num)
????????????????case?1..<2?:?result?=?"B";?break
????????????????//?調(diào)用Range的isCase方法,?相當(dāng)于?(1..<2).isCase(num)
????????????????case?2..4?:?result?=?"C";?break
????????????????//?調(diào)用閉包的isCase方法,?相當(dāng)于?{it%5==0}.isCase(num)
????????????????case?{it%5==0}?:?result?=?"D";?break
????????????????//?調(diào)用列表的isCase方法,?相當(dāng)于?[11,31].isCase(num)
????????????????case?[11,31]?:?result?=?"E";?break
????????????????//?調(diào)用Integer的isCase方法,?相當(dāng)于?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"
????}
}
參考文獻(xiàn)
Groovy In Action · 2nd Edition ? Dierk K?nig、Guillaume Laforge著
