Groovy基础语法 - 标识符&字符串

1.标识符

1.1普通标识符

标识符以字母、美元或下划线开头。它们不能以数字开头。
字母可以介于以下范围内:

  • “a” 到 “z”(小写 ascii 字母)
  • “A”到“Z”(大写的 ascii 字母)
  • “\u00C0”到“\u00D6”
  • “\u00D8”到“\u00F6”
  • “\u00F8”到“\u00FF”
  • “\u0100”到“\uFFFE”

然后,以下字符可以包含字母和数字。
以下是有效标识符的几个示例(此处为变量名称):

def name
def item3
def with_underscore
def $dollarStart

但以下标识符无效:

def 3tier
def a+b
def a#b

当跟随点时,所有关键字也是有效的标识符:

foo.as
foo.assert
foo.break
foo.case
foo.catch

1.2带引号的标识符

带引号的标识符显示在虚线表达式的点之后。例如,person.name表达式的name可以用 person.'name'person."name"来表示。当某些标识符包含 Java 语言规范禁止的非法字符,但 Groovy 在引用这些字符时允许这些字符时,这一点特别有趣。例如,破折号、空格、感叹号等字符,例如:

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提供了不同的字符串文本。实际上,所有类型的字符串都允许在点之后:

map.'single quote'
map."double quote"
map.'''triple single quote'''
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$

普通字符串和Groovy的GStrings(插值字符串)之间存在差异,因为在后一种情况下,内插值插入到最终字符串中以评估整个标识符:

def firstname = "Homer"
map."Simpson-${firstname}" = "Homer Simpson"

assert map.'Simpson-Homer' == "Homer Simpson"  //true

2.字符串

文本文本以称为字符串的字符串链的形式表示。Groovy允许您实例化 java.lang.String 对象,以及GStrings(groovy.lang.GString),这在其他编程语言中也称为插值字符串。

2.1单引号字符串

单引号字符串是一系列用单引号括起来的字符:

'a single-quoted string'

单引号字符串是普通的java.lang.String,不支持插值。

2.2字符串连接

所有 Groovy 字符串都可以与运算符+连接:

assert 'ab' == 'a' + 'b'  //true

2.3三重单引号字符串

'''a triple-single-quoted string'''

三重单引号字符串是普通的java.lang.String,不支持插值。

三重单引号字符串可能跨越多行。字符串的内容可以跨越行边界,而无需将字符串拆分为多个部分,也无需串联或换行符转义字符:

def aMultilineString = '''line one
line two
line three'''

如果代码缩进(例如在类的方法正文中),则字符串将包含缩进的空格。Groovy 开发工具包包含用于使用String#stripIndent()方法去除缩进的方法,以及使用采用分隔符字符来标识要从字符串开头删除的文本的String#stripMargin()方法。

创建字符串时,如下所示:

def startingAndEndingWithANewline = '''
line one
line two
line three
'''

会注意到,生成的字符串包含换行符作为第一个字符。可以通过用反斜杠转义换行符来剥离该字符:

def strippedFirstNewline = '''\
line one
line two
line three
'''

assert !strippedFirstNewline.startsWith('\n')  //true

2.3双引号字符串

双引号字符串是一系列用双引号括起来的字符:

"a double-quoted string"

如果没有java.lang.String内插表达式,双引号字符串是纯文本的,但如果存在groovy.lang.GString内插,则为实例。
要转义双引号,可以使用反斜杠字符:“双引号:\”。

2.3.1字符串插值

任何 Groovy 表达式都可以在所有字符串文本中进行插值,单引号和三重单引号字符串除外。插值是在计算字符串时将字符串中的占位符替换为其值的操作。占位符表达式由 ${}包围。对于明确的虚线表达式,可以省略大括号,即在这些情况下我们可以只使用$ 前缀。如果将 GString 传递给采用 String 的方法,则占位符内的表达式值将计算为其字符串表示形式(通过调用toString()该表达式),并将生成的 String 传递给该方法。
在这里,我们有一个字符串,其中包含一个引用局部变量的占位符:

def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"

assert greeting.toString() == 'Hello Guillaume'  //true

任何Groovy表达式都是有效的,正如我们在这个例子中看到的算术表达式:

def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5'  //true

不仅允许在${}占位符之间使用表达式,而且语句也允许使用。但是,语句的值只是null 。因此,如果在该占位符中插入了多个语句,则最后一个语句应以某种方式返回要插入的有意义的值。例如,“1 和 2 之和等于 ${def a = 1;def b = 2;a + b}“受支持并按预期工作,但一个好的做法通常是坚持使用 GString 占位符内的简单表达式。

除了${}占位符之外,我们还可以使用一个单独的符号$作为虚线表达式的前缀:

def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'  //true

但只有 a.ba.b.c等形式的虚线表达式 才有效。包含括号(如方法调用)、闭包的大括号、不属于属性表达式的点或算术运算符的表达式将无效。给定以下数字的变量定义:

def number = 3.14

以下语句将抛出一个groovy.lang.MissingPropertyException,因为Groovy认为您正在尝试访问该数字的toString属性,该属性不存在。
同样,如果表达式不明确,则需要保留大括号:

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

如果需要转义 GString 中的 ${}$占位符,以便它们按原样显示而不进行插值,则只需使用反斜杠字符来转义美元符号:

assert '$5' == "\$5"
assert '${name}' == "\${name}"

2.3.2插值闭包表达式的特殊情况

到目前为止,我们已经看到我们可以在${}占位符内插入任意表达式,但是闭包表达式有一个${→}表示法。当占位符包含箭头时,表达式实际上是一个闭包表达式 — 您可以将其视为前面有一个美元前缀的闭包:

def sParameterLessClosure = "1 + 2 == ${-> 3}" 
assert sParameterLessClosure == '1 + 2 == 3'  //true

def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" 
assert sOneParamClosure == '1 + 2 == 3'   //true

闭包是一个无参数的闭包,它不接受参数
此处,闭包采用单个java.io.StringWriter参数,您可以使用<< leftShift 运算符向其追加内容。在任一情况下,两个占位符都是嵌入式闭包。

从外观上看,它看起来像是定义要插值的表达式的一种更冗长的方式,但与单纯表达式相比,闭包具有一个有趣的优势:惰性求值。

让我们考虑以下示例:

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的互操作性

当一个方法(无论是在Java还是Groovy中实现)需要一个java.lang.String,但是我们传递一个groovy.lang.GString实例时,GString的方法会自动透明地调用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
  • 我们创建一个 GString 变量
  • 我们仔细检查它是否是 GString 的一个实例
  • 然后,我们将该 GString 传递给一个将 String 作为参数的方法。
  • taskString()该方法的签名明确表示其唯一参数是 String
  • 我们还验证该参数确实是字符串而不是 GString。

2.3.4GString & String hashCodes

虽然可以使用内插字符串来代替普通的Java字符串,但它们与字符串在特定方面有所不同:它们的哈希码是不同的。普通 Java 字符串是不可变的,而生成的 GString 字符串表示形式可能会有所不同,具体取决于其内插值。即使对于相同的结果字符串,GStrings 和 Strings 也不具有相同的哈希代码。

assert "one: ${1}".hashCode() != "one: 1".hashCode() //fasle

应该避免使用GString作为Map键的具有不同哈希值的GString和字符串,特别是当我们尝试检索具有字符串而不是GString的关联值时。

def key = "a"
def m = ["${key}": "letter ${key}"]     

assert m["a"] == null //true
  • 使用初始对创建映射,其键为 GString
  • 当我们尝试使用 String 键获取值时,我们将找不到它,因为 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

双引号和单引号都不需要在三重双引号字符串中转义。

2.4.1转义特殊字符

您可以使用反斜杠字符转义单引号,以避免终止字符串文本:

'an escaped single quote: \' needs a backslash'

您可以使用双反斜杠转义转义字符本身:

'an escaped escape character: \\ needs a double backslash'

一些特殊字符还使用反斜杠作为转义字符:


转义字符.png

2.4.2统一码转义序列

对于键盘上不存在的字符,可以使用 unicode 转义序列:反斜杠,后跟“u”,然后是 4 个十六进制数字。

例如,欧元货币符号可以用以下方式表示:

'The Euro currency symbol: \u20AC'

2.5斜线

除了通常的带引号的字符串之外,Groovy还提供斜杠字符串,用作开始和结束分隔符。斜杠字符串对于定义正则表达式和模式特别有用,因为不需要转义反斜杠
斜杠字符串的示例:

def fooPattern = /.*foo.*/
assert fooPattern == '.*foo.*'  //true

只有正斜杠需要用反斜杠进行转义:

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')

斜杠字符串可以被认为是定义GString的另一种方式,但具有不同的转义规则。因此,它们支持插值:

def color = 'blue'
def interpolatedSlashy = /a ${color} car/

assert interpolatedSlashy == 'a blue car'  //true

2.5.1特殊情况

空斜杠字符串不能用双正斜杠表示,因为Groovy解析器将其理解为行注释。这就是为什么下面的断言实际上不会编译,因为它看起来像一个非终止语句:

assert '' == //     //compilation error

由于斜杠字符串主要是为了让正则表达式更容易而设计的,因此GStrings中的一些错误就像$()$5将适用于斜杠字符串。
请记住,转义反斜杠不是必需的。另一种思考方式是,实际上不支持逃避。斜杠字符串/\t/将不包含制表符,而是包含后跟字符“t”的反斜杠。仅允许斜杠字符转义,即 /\/folder/将是一个包含 '/folder'的斜杠字符串。斜杠转义的结果是斜杠字符串不能以反斜杠结尾。否则,这将转义斜杠字符串终止符。您可以改用一个特殊的技巧./ends with slash ${'\'}/,但最好避免在这种情况下使用斜杠字符串。

2.6美元斜线

美元斜线字符串是多行GStrings,用$/开口和/$结束分隔。转义字符是美元符号,它可以转义另一美元或正斜杠。只有在与特殊使用发生冲突时才需要转义美元和正斜杠字符。这些字符通常表示GString占位符,因此$foo可以通过转义美元将这四个字符输入到美元斜杠字符串中,即$$foo.同样,如果您希望一个美元斜杠的收尾分隔符出现在字符串中,则需要对其进行转义。
以下是一些示例:

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

它的创建是为了克服斜杠字符串转义规则的一些限制。当它的转义规则适合您的字符串内容时使用它(通常,如果它有一些您不想转义的斜杠)。


2.7字符串汇总表

汇总表.png

2.8字符

与Java不同,Groovy没有明确的字符文字。但是,您可以通过三种不同的方式明确地使Groovy字符串成为实际字符:

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容