1.標(biāo)識符
1.1普通標(biāo)識符
標(biāo)識符以字母、美元或下劃線開頭。它們不能以數(shù)字開頭。
字母可以介于以下范圍內(nèi):
- “a” 到 “z”(小寫 ascii 字母)
- “A”到“Z”(大寫的 ascii 字母)
- “\u00C0”到“\u00D6”
- “\u00D8”到“\u00F6”
- “\u00F8”到“\u00FF”
- “\u0100”到“\uFFFE”
然后,以下字符可以包含字母和數(shù)字。
以下是有效標(biāo)識符的幾個(gè)示例(此處為變量名稱):
def name
def item3
def with_underscore
def $dollarStart
但以下標(biāo)識符無效:
def 3tier
def a+b
def a#b
當(dāng)跟隨點(diǎn)時(shí),所有關(guān)鍵字也是有效的標(biāo)識符:
foo.as
foo.assert
foo.break
foo.case
foo.catch
1.2帶引號的標(biāo)識符
帶引號的標(biāo)識符顯示在虛線表達(dá)式的點(diǎn)之后。例如,person.name表達(dá)式的name可以用 person.'name'或 person."name"來表示。當(dāng)某些標(biāo)識符包含 Java 語言規(guī)范禁止的非法字符,但 Groovy 在引用這些字符時(shí)允許這些字符時(shí),這一點(diǎn)特別有趣。例如,破折號、空格、感嘆號等字符,例如:
def map = [:]
map."an identifier with a space and double quotes" = "ALLOWED"
map.'with-dash-signs-and-single-quotes' = "ALLOWED"
assert map."an identifier with a space and double quotes" == "ALLOWED" //true
assert map.'with-dash-signs-and-single-quotes' == "ALLOWED" //true
Groovy提供了不同的字符串文本。實(shí)際上,所有類型的字符串都允許在點(diǎn)之后:
map.'single quote'
map."double quote"
map.'''triple single quote'''
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$
普通字符串和Groovy的GStrings(插值字符串)之間存在差異,因?yàn)樵诤笠环N情況下,內(nèi)插值插入到最終字符串中以評估整個(gè)標(biāo)識符:
def firstname = "Homer"
map."Simpson-${firstname}" = "Homer Simpson"
assert map.'Simpson-Homer' == "Homer Simpson" //true
2.字符串
文本文本以稱為字符串的字符串鏈的形式表示。Groovy允許您實(shí)例化 java.lang.String 對象,以及GStrings(groovy.lang.GString),這在其他編程語言中也稱為插值字符串。
2.1單引號字符串
單引號字符串是一系列用單引號括起來的字符:
'a single-quoted string'
單引號字符串是普通的
java.lang.String,不支持插值。
2.2字符串連接
所有 Groovy 字符串都可以與運(yùn)算符+連接:
assert 'ab' == 'a' + 'b' //true
2.3三重單引號字符串
'''a triple-single-quoted string'''
三重單引號字符串是普通的
java.lang.String,不支持插值。
三重單引號字符串可能跨越多行。字符串的內(nèi)容可以跨越行邊界,而無需將字符串拆分為多個(gè)部分,也無需串聯(lián)或換行符轉(zhuǎn)義字符:
def aMultilineString = '''line one
line two
line three'''
如果代碼縮進(jìn)(例如在類的方法正文中),則字符串將包含縮進(jìn)的空格。Groovy 開發(fā)工具包包含用于使用String#stripIndent()方法去除縮進(jìn)的方法,以及使用采用分隔符字符來標(biāo)識要從字符串開頭刪除的文本的String#stripMargin()方法。
創(chuàng)建字符串時(shí),如下所示:
def startingAndEndingWithANewline = '''
line one
line two
line three
'''
會(huì)注意到,生成的字符串包含換行符作為第一個(gè)字符??梢酝ㄟ^用反斜杠轉(zhuǎn)義換行符來剝離該字符:
def strippedFirstNewline = '''\
line one
line two
line three
'''
assert !strippedFirstNewline.startsWith('\n') //true
2.3雙引號字符串
雙引號字符串是一系列用雙引號括起來的字符:
"a double-quoted string"
如果沒有
java.lang.String內(nèi)插表達(dá)式,雙引號字符串是純文本的,但如果存在groovy.lang.GString內(nèi)插,則為實(shí)例。
要轉(zhuǎn)義雙引號,可以使用反斜杠字符:“雙引號:\”。
2.3.1字符串插值
任何 Groovy 表達(dá)式都可以在所有字符串文本中進(jìn)行插值,單引號和三重單引號字符串除外。插值是在計(jì)算字符串時(shí)將字符串中的占位符替換為其值的操作。占位符表達(dá)式由 ${}包圍。對于明確的虛線表達(dá)式,可以省略大括號,即在這些情況下我們可以只使用$ 前綴。如果將 GString 傳遞給采用 String 的方法,則占位符內(nèi)的表達(dá)式值將計(jì)算為其字符串表示形式(通過調(diào)用toString()該表達(dá)式),并將生成的 String 傳遞給該方法。
在這里,我們有一個(gè)字符串,其中包含一個(gè)引用局部變量的占位符:
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
assert greeting.toString() == 'Hello Guillaume' //true
任何Groovy表達(dá)式都是有效的,正如我們在這個(gè)例子中看到的算術(shù)表達(dá)式:
def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5' //true
不僅允許在
${}占位符之間使用表達(dá)式,而且語句也允許使用。但是,語句的值只是null。因此,如果在該占位符中插入了多個(gè)語句,則最后一個(gè)語句應(yīng)以某種方式返回要插入的有意義的值。例如,“1 和 2 之和等于 ${def a = 1;def b = 2;a + b}“受支持并按預(yù)期工作,但一個(gè)好的做法通常是堅(jiān)持使用 GString 占位符內(nèi)的簡單表達(dá)式。
除了${}占位符之外,我們還可以使用一個(gè)單獨(dú)的符號$作為虛線表達(dá)式的前綴:
def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old' //true
但只有 a.b、a.b.c等形式的虛線表達(dá)式 才有效。包含括號(如方法調(diào)用)、閉包的大括號、不屬于屬性表達(dá)式的點(diǎn)或算術(shù)運(yùn)算符的表達(dá)式將無效。給定以下數(shù)字的變量定義:
def number = 3.14
以下語句將拋出一個(gè)groovy.lang.MissingPropertyException,因?yàn)镚roovy認(rèn)為您正在嘗試訪問該數(shù)字的toString屬性,該屬性不存在。
同樣,如果表達(dá)式不明確,則需要保留大括號:
String thing = 'treasure'
assert 'The x-coordinate of the treasure is represented by treasure.x' ==
"The x-coordinate of the $thing is represented by $thing.x" // <= Not allowed: ambiguous!!
assert 'The x-coordinate of the treasure is represented by treasure.x' ==
"The x-coordinate of the $thing is represented by ${thing}.x" // <= Curly braces required
如果需要轉(zhuǎn)義 GString 中的 ${}或 $占位符,以便它們按原樣顯示而不進(jìn)行插值,則只需使用反斜杠字符來轉(zhuǎn)義美元符號:
assert '$5' == "\$5"
assert '${name}' == "\${name}"
2.3.2插值閉包表達(dá)式的特殊情況
到目前為止,我們已經(jīng)看到我們可以在${}占位符內(nèi)插入任意表達(dá)式,但是閉包表達(dá)式有一個(gè)${→}表示法。當(dāng)占位符包含箭頭時(shí),表達(dá)式實(shí)際上是一個(gè)閉包表達(dá)式 — 您可以將其視為前面有一個(gè)美元前綴的閉包:
def sParameterLessClosure = "1 + 2 == ${-> 3}"
assert sParameterLessClosure == '1 + 2 == 3' //true
def sOneParamClosure = "1 + 2 == ${ w -> w << 3}"
assert sOneParamClosure == '1 + 2 == 3' //true
閉包是一個(gè)無參數(shù)的閉包,它不接受參數(shù)
此處,閉包采用單個(gè)java.io.StringWriter參數(shù),您可以使用<<leftShift 運(yùn)算符向其追加內(nèi)容。在任一情況下,兩個(gè)占位符都是嵌入式閉包。
從外觀上看,它看起來像是定義要插值的表達(dá)式的一種更冗長的方式,但與單純表達(dá)式相比,閉包具有一個(gè)有趣的優(yōu)勢:惰性求值。
讓我們考慮以下示例:
def number = 1
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"
assert eagerGString == "value == 1" //true
assert lazyGString == "value == 1" //true
number = 2
assert eagerGString == "value == 1" //true
assert lazyGString == "value == 2" //true
2.3.3與java的互操作性
當(dāng)一個(gè)方法(無論是在Java還是Groovy中實(shí)現(xiàn))需要一個(gè)java.lang.String,但是我們傳遞一個(gè)groovy.lang.GString實(shí)例時(shí),GString的方法會(huì)自動(dòng)透明地調(diào)用toString()。
String takeString(String message) {
assert message instanceof String
return message
}
def message = "The message is ${'hello'}"
assert message instanceof GString //true
def result = takeString(message)
assert result instanceof String //true
assert result == 'The message is hello' //true
- 我們創(chuàng)建一個(gè) GString 變量
- 我們仔細(xì)檢查它是否是 GString 的一個(gè)實(shí)例
- 然后,我們將該 GString 傳遞給一個(gè)將 String 作為參數(shù)的方法。
-
taskString()該方法的簽名明確表示其唯一參數(shù)是 String - 我們還驗(yàn)證該參數(shù)確實(shí)是字符串而不是 GString。
2.3.4GString & String hashCodes
雖然可以使用內(nèi)插字符串來代替普通的Java字符串,但它們與字符串在特定方面有所不同:它們的哈希碼是不同的。普通 Java 字符串是不可變的,而生成的 GString 字符串表示形式可能會(huì)有所不同,具體取決于其內(nèi)插值。即使對于相同的結(jié)果字符串,GStrings 和 Strings 也不具有相同的哈希代碼。
assert "one: ${1}".hashCode() != "one: 1".hashCode() //fasle
應(yīng)該避免使用GString作為Map鍵的具有不同哈希值的GString和字符串,特別是當(dāng)我們嘗試檢索具有字符串而不是GString的關(guān)聯(lián)值時(shí)。
def key = "a"
def m = ["${key}": "letter ${key}"]
assert m["a"] == null //true
- 使用初始對創(chuàng)建映射,其鍵為 GString
- 當(dāng)我們嘗試使用 String 鍵獲取值時(shí),我們將找不到它,因?yàn)?Strings 和 GString 具有不同的哈希值
2.4三重雙引號字符串
三重雙引號字符串的行為類似于雙引號字符串,此外,它們是多行的,就像三重單引號字符串一樣。
def name = 'Groovy'
def template = """
Dear Mr ${name},
You're the winner of the lottery!
Yours sincerly,
Dave
"""
assert template.toString().contains('Groovy') //true
雙引號和單引號都不需要在三重雙引號字符串中轉(zhuǎn)義。
2.4.1轉(zhuǎn)義特殊字符
您可以使用反斜杠字符轉(zhuǎn)義單引號,以避免終止字符串文本:
'an escaped single quote: \' needs a backslash'
您可以使用雙反斜杠轉(zhuǎn)義轉(zhuǎn)義字符本身:
'an escaped escape character: \\ needs a double backslash'
一些特殊字符還使用反斜杠作為轉(zhuǎn)義字符:

2.4.2統(tǒng)一碼轉(zhuǎn)義序列
對于鍵盤上不存在的字符,可以使用 unicode 轉(zhuǎn)義序列:反斜杠,后跟“u”,然后是 4 個(gè)十六進(jìn)制數(shù)字。
例如,歐元貨幣符號可以用以下方式表示:
'The Euro currency symbol: \u20AC'
2.5斜線
除了通常的帶引號的字符串之外,Groovy還提供斜杠字符串,用作開始和結(jié)束分隔符。斜杠字符串對于定義正則表達(dá)式和模式特別有用,因?yàn)椴恍枰D(zhuǎn)義反斜杠
斜杠字符串的示例:
def fooPattern = /.*foo.*/
assert fooPattern == '.*foo.*' //true
只有正斜杠需要用反斜杠進(jìn)行轉(zhuǎn)義:
def escapeSlash = /The character \/ is a forward slash/
assert escapeSlash == 'The character / is a forward slash' //true
斜線字符串是多行的:
def multilineSlashy = /one
two
three/
assert multilineSlashy.contains('\n')
斜杠字符串可以被認(rèn)為是定義GString的另一種方式,但具有不同的轉(zhuǎn)義規(guī)則。因此,它們支持插值:
def color = 'blue'
def interpolatedSlashy = /a ${color} car/
assert interpolatedSlashy == 'a blue car' //true
2.5.1特殊情況
空斜杠字符串不能用雙正斜杠表示,因?yàn)镚roovy解析器將其理解為行注釋。這就是為什么下面的斷言實(shí)際上不會(huì)編譯,因?yàn)樗雌饋硐褚粋€(gè)非終止語句:
assert '' == // //compilation error
由于斜杠字符串主要是為了讓正則表達(dá)式更容易而設(shè)計(jì)的,因此GStrings中的一些錯(cuò)誤就像$()或$5將適用于斜杠字符串。
請記住,轉(zhuǎn)義反斜杠不是必需的。另一種思考方式是,實(shí)際上不支持逃避。斜杠字符串/\t/將不包含制表符,而是包含后跟字符“t”的反斜杠。僅允許斜杠字符轉(zhuǎn)義,即 /\/folder/將是一個(gè)包含 '/folder'的斜杠字符串。斜杠轉(zhuǎn)義的結(jié)果是斜杠字符串不能以反斜杠結(jié)尾。否則,這將轉(zhuǎn)義斜杠字符串終止符。您可以改用一個(gè)特殊的技巧./ends with slash ${'\'}/,但最好避免在這種情況下使用斜杠字符串。
2.6美元斜線
美元斜線字符串是多行GStrings,用$/開口和/$結(jié)束分隔。轉(zhuǎn)義字符是美元符號,它可以轉(zhuǎn)義另一美元或正斜杠。只有在與特殊使用發(fā)生沖突時(shí)才需要轉(zhuǎn)義美元和正斜杠字符。這些字符通常表示GString占位符,因此$foo可以通過轉(zhuǎn)義美元將這四個(gè)字符輸入到美元斜杠字符串中,即$$foo.同樣,如果您希望一個(gè)美元斜杠的收尾分隔符出現(xiàn)在字符串中,則需要對其進(jìn)行轉(zhuǎn)義。
以下是一些示例:
def name = "Guillaume"
def date = "April, 1st"
def dollarSlashy = $/
Hello $name,
today we're ${date}.
$ dollar sign
$$ escaped dollar sign
\ backslash
/ forward slash
$/ escaped forward slash
$$$/ escaped opening dollar slashy
$/$$ escaped closing dollar slashy
/$
assert [
'Guillaume',
'April, 1st',
'$ dollar sign',
'$ escaped dollar sign',
'\\ backslash',
'/ forward slash',
'/ escaped forward slash',
'$/ escaped opening dollar slashy',
'/$ escaped closing dollar slashy'
].every { dollarSlashy.contains(it) } //all true
它的創(chuàng)建是為了克服斜杠字符串轉(zhuǎn)義規(guī)則的一些限制。當(dāng)它的轉(zhuǎn)義規(guī)則適合您的字符串內(nèi)容時(shí)使用它(通常,如果它有一些您不想轉(zhuǎn)義的斜杠)。
2.7字符串匯總表

2.8字符
與Java不同,Groovy沒有明確的字符文字。但是,您可以通過三種不同的方式明確地使Groovy字符串成為實(shí)際字符:
char c1 = 'A'
assert c1 instanceof Character //true
def c2 = 'B' as char
assert c2 instanceof Character //true
def c3 = (char)'C'
assert c3 instanceof Character //true