1. 概述
在本教程中,我们将深入探讨 Groovy 中的多种字符串类型,包括单引号、双引号、三引号以及斜杠字符串(slashy strings)。
我们还会涉及 Groovy 字符串对特殊字符、多行文本、正则表达式、转义和变量插值的支持。
2. 对 java.lang.String
的增强
Groovy 是基于 Java 的语言,因此它天然具备 Java 中所有 String
的能力,比如字符串拼接、丰富的 API 接口,以及字符串常量池带来的性能优势。
不过,Groovy 在这些基础之上进行了扩展,提供了更多灵活的语法支持。
2.1. 字符串拼接
字符串拼接是将两个字符串连接在一起的操作:
def first = 'first'
def second = "second"
def concatenation = first + second
assertEquals('firstsecond', concatenation)
Groovy 支持不同类型的字符串之间进行拼接操作 ✅。
2.2. 字符串插值
Java 只能通过 printf
实现基本的模板功能,而 Groovy 提供了更强大的 字符串插值 功能,允许在字符串中嵌入变量:
def name = "Kacper"
def result = "Hello ${name}!"
assertEquals("Hello Kacper!", result.toString())
⚠️ 虽然各种字符串都支持拼接,但只有特定类型的字符串才支持插值。
2.3. GString
上面的例子中有个小细节需要注意:为什么我们要调用 .toString()
?
其实,result
并不是 String
类型,而是 Groovy 自己实现的一个类 —— **GString
**。
由于 Java 的 String
是 final
类型,Groovy 无法继承它来添加插值功能。因此,Groovy 使用了自己的字符串类型 GString
来实现插值。
例如:
assertEquals("Hello Kacper!", result)
这会触发 assertEquals(Object, Object)
方法,并抛出异常:
java.lang.AssertionError: expected: java.lang.String<Hello Kacper!>
but was: org.codehaus.groovy.runtime.GStringImpl<Hello Kacper!>
Expected :java.lang.String<Hello Kacper!>
Actual :org.codehaus.groovy.runtime.GStringImpl<Hello Kacper!>
3. 单引号字符串
单引号字符串是最简单的字符串类型:
def example = 'Hello world'
它们本质上就是 Java 的 String
,适用于需要包含引号的场景,避免使用转义字符:
def easyToRead = 'Kacper loves "Lord of the Rings"'
4. 三重单引号字符串
三重单引号字符串非常适合表示多行内容,比如 JSON 或 SQL:
def jsonContent = '''
{
"name": "John",
"age": 20,
"birthDate": null
}
'''
4.1. 新行处理
默认情况下,三重单引号字符串会保留首尾的新行符:
def triple = '''
firstline
secondline
'''
实际内容为:
(newline)
firstline(newline)
secondline(newline)
可以通过在首尾加上反斜杠 \
来去掉首行的换行:
def triple = '''\
firstline
secondline
'''
4.2. 去除缩进
为了保持代码整洁又不影响字符串内容,可以使用 stripIndent()
方法:
def triple = '''\
firstline
secondline'''.stripIndent()
assertEquals("firstline\nsecondline", triple)
4.3. 相对缩进
stripIndent()
会根据最短非空行的缩进作为基准:
def triple = '''\
firstline
secondline\
'''.stripIndent()
输出结果为:
firstline
secondline
4.4. 使用 stripMargin()
还可以使用 |
和 stripMargin()
来控制起始位置:
def triple = '''\
|firstline
|secondline'''.stripMargin()
输出:
firstline
secondline
4.5. 转义特殊字符
虽然三重单引号字符串不需要转义双引号,但仍需转义单引号和反斜杠:
常见需要转义的字符包括:
\t
– 制表符\n
– 换行符\b
– 退格\r
– 回车\\
– 反斜杠\f
– 换页\'
– 单引号
示例:
def specialCharacters = '''hello \'John\'. This is backslash - \\ \nSecond line starts here'''
5. 双引号字符串
双引号字符串同样也是 Java 的 String
,但它支持插值功能。
5.1. GString 与延迟求值
当双引号字符串中含有插值时,Groovy 会自动将其转换为 GString
:
def 'String ang GString'() {
given:
def string = "example"
def stringWithExpression = "example${2}"
expect:
string instanceof String
stringWithExpression instanceof GString
stringWithExpression.toString() instanceof String
}
5.2. 插值变量引用
最常用的插值方式是引用变量:
def 'placeholder with variable'() {
given:
def name = "John"
when:
def helloName = "Hello $name!".toString()
then:
helloName == "Hello John!"
}
5.3. 插值表达式
也可以插入表达式:
def 'placeholder with expression'() {
given:
def result = "result is ${2 * 2}".toString()
expect:
result == "result is 4"
}
⚠️ 不建议在插值中写复杂语句。
5.4. 对象属性访问
可以通过点号访问对象属性:
def 'placeholder with dotted access'() {
given:
def person = [name: 'John']
when:
def myNameIs = "I'm $person.name, and you?".toString()
then:
myNameIs == "I'm John, and you?"
}
调用方法时必须使用 ${}
:
def 'placeholder with method call'() {
given:
def name = 'John'
when:
def result = "Uppercase name: ${name.toUpperCase()}".toString()
then:
result == "Uppercase name: JOHN"
}
5.5. GString 与 String 的 hashCode 差异
⚠️ GString
和 String
的 hashCode
不一致:
def 'GString and String hashcode'() {
given:
def string = "2+2 is 4"
def gstring = "2+2 is ${4}"
expect:
string.hashCode() != gstring.hashCode()
}
❌ 所以不要将 GString
作为 Map 的 key!
6. 三重双引号字符串
结合双引号和三引号的优点,支持多行插值:
def name = "John"
def multiLine = """
I'm $name.
"This is quotation from 'War and Peace'"
"""
✅ 不需要转义单引号或双引号。
7. 斜杠字符串(Slashy String)
斜杠字符串主要用于正则表达式,无需转义反斜杠:
def pattern = /\d{3}\s\w+\s\w+\\\w+/
assertTrue("3 Blind Mice\Men".matches(pattern))
✅ 支持插值和多行:
def name = 'John'
def example = /
Dear ([A-Z]+),
Love, $name
/
⚠️ 需要转义斜杠 /
:
def pattern = /.*foobar.*\/hello.*/
❌ 不能表示空字符串,因为 //
会被当作注释。
8. 美元斜杠字符串(Dollar-Slashy String)
用于避免频繁转义斜杠 /
,以 $/$
开始和结束:
def name = "John"
def dollarSlashy = $/
Hello $name!,
I can show you a $ sign or an escaped dollar sign: $$
Both slashes work: \ or /, but we can still escape it: $/
We have to escape opening and closing delimiters:
- $$$/
- $/$$
/$
输出:
Hello John!,
I can show you a $ sign or an escaped dollar sign: $
Both slashes work: \ or /, but we can still escape it: /
We have to escape opening and closing delimiter:
- $/
- /$
9. 字符类型(Character)
Groovy 没有显式的字符字面量,单引号默认是字符串。
要创建字符,有三种方式:
- 显式声明为
char
- 使用
as
运算符 - 强制类型转换为
char
def 'character'() {
given:
char a = 'A' as char
char b = 'B' as char
char c = (char) 'C'
expect:
a instanceof Character
b instanceof Character
c instanceof Character
}
10. 总结
快速回顾一下重点:
- 单引号字符串不支持插值 ❌
- 斜杠和三重双引号支持多行 ✅
- 多行字符串包含空白字符,需手动清理 ⚠️
- 除美元斜杠字符串外,其他字符串都使用
\
转义 ✅
11. 结论
本文介绍了 Groovy 中的多种字符串类型及其特性,包括多行支持、插值、正则表达式等。如需了解更多 Groovy 特性,请参考我们的 Groovy 语言入门指南。