Groovy基礎(chǔ)語法 - 標(biāo)識符&字符串

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)義字符:


轉(zhuǎn)義字符.png

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字符串匯總表

匯總表.png

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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容