Groovy 字符串
官方文档:http://www.groovy-lang.org/syntax.html
Groovy有java.lang.String和groovy.lang.GString两种字符串对象类型,具体如下
单引号字符串
单引号字符串是java.lang.String类型的,不支持占位符插值操作
def name = 'Groovy!'
def body = 'test $name'
println(body); //test $name,不是 test Groovy!
Groovy的字符串可以通过 "+" 直接拼接
assert 'groovy' == 'g' + 'roo' + 'vy';
println('ab\'' + 'f'); // ab'f
转义字符规则同Java一样
三重单引号字符串
三重单引号字符串也是java.lang.String类型的,不支持占位符插值操作,可以表示多行字符串
def str = '''line one
line two
line three'''
如果在换行之前加个,则相当于没有换行
def str = '''\
line one aaa \n kkkk
line tow 2\
line three'''
println(str);
运行结果:
line one aaa
kkkk
line tow 2line three
双引号字符串
双引号字符串支持占位插值操作,如果双引号字符串中不包含占位符则是java.lang.String类型的,如果包含则是groovy.lang.GString类型的。
占位符可以用${}
或$
来表示,${}
用于替换一般字符串或表达式,$
主要用于A.B的形式中
// ${}
def str1 = 'Groovy!'
def str2 = "hello ${str1}"
println(str2); // hello Groovy!
def str3 = "the sum of 2 and 3 is ${2 + 3}"
println(str3); // the sum of 2 and 3 is 5
// $
def str4 = "hello $str1"
println(str4); // hello Groovy!
def map = ['name':'Tom', 'age':null, 'gender':null]
println("$map.name is $map.age years old, gender $map.gender"); // Tom is null years old, gender null
$
只对a.b或a.b.c形式有效,如果表达式包含括号类似于方法调用、闭包的大括号或算术运算符是无效的。
def str = "wzx"
println("my name length is ${str.length()}"); //my name length is 3
println("my name length is $str.length()");
//该代码运行抛出groovy.lang.MissingPropertyException异常,因为Groovy认为去访问num的toString的属性,所以异常
def closure = {"sum " + it};
def str1 = "2 + 3 = ${closure(5)}"
def str2 = "2 + 3 = $closure(5)"
println(str1); // 2 + 3 = sum 5
println(str2); // 2 + 3 = (5) 错误
注意,在表达式中访问属性前必须保证属性已经定义好(值为空也可以),如果使用了未定义的属性会抛出groovy.lang.MissingPropertyException异常
如果需要表示$
or ${}
字符串,可以使用反斜杠\
assert '${name}' == '\${name}'
插值闭包表达式的特殊情况
到目前为止,我们已经可以在${}
占位符插值任意表达式。但是对于闭包有一个特殊情况和符号,占位符包含一个箭头${→}
时,表达式实际上是一个闭包表达式,可以认为它是一个闭包前缀了一个美元符号:
//无参闭包
def strWithNoParmClosure = "2 + 3 = ${ -> 5}" // 2 + 3 = 5
//带有一个 java.io.StringWriter 参数的闭包,可通过 << 操作符将内容传给参数
def strWithParmClosure = "1 + 2 != ${parm -> parm << 'six'}" // 1 + 2 != six
以上两种都是占位符插值闭包的情况,看起来比单纯的表达式定义麻烦,但是闭包有一个特性:延迟运算(lazy evaluation)
def number = 1;
def eargeGString = "value = ${number}"
def lazyGString = "value = ${ -> number}"
assert eargeGString == "value = 1"
assert lazyGString == "value = 1"
number = 2;
assert eargeGString == "value = 1"
assert lazyGString == "value = 2"
对于普通插值表达式(eargeGString),其值实际在表达式创建时就被绑定了,如:
def num1 = 9, num2 = 11;
def fun(int p1, int p2){
p1 + p2
}
def strWithParmClosure = "xxx ${fun(num1, num2)}";
println(strWithParmClosure); // xxx 20
num2 = 20;
println(strWithParmClosure); //xxx 20
但是对于有闭包的表达式,在每次GString转成String时闭包都会被调用。
插值闭包表达式中的闭包只允许有0或1个参数,否则运行时会抛异常
groovy> def strWithParmClosure = "2 + 3 != ${parm, parm2 -> (parm << 2) + (parm2 << 3)}";
groovy> println(strWithParmClosure);
运行时异常:
groovy.lang.GroovyRuntimeException: Trying to evaluate a GString containing a Closure taking 2 parameters
GString & String hashCodes
String是不可变的,GString的String表示会根据插值变化,即使String和GString有相同的字符串,其hashCode也不同
assert "one: ${1}".hashCode() != "one: 1".hashCode()
所以应该避免使用GString作为Map key,例如:
def key = "a"
def m = ["${key}": "letter ${key}"]
assert m["a"] == null //由于key的HashCode不同,所以取不到
三重双引号字符串
三重双引号字符串表现得像双引号字符串,另外它们是多行的
def name = 'Groovy'
def template = """
Dear Mr ${name},\
You're "the winner" of 'the lottery'!
Yours sincerly,\
Dave
\"AAABBBB\\"
"""
assert template.contains("Groovy")
println(template);
运行结果:
...第一行为空行
Dear Mr Groovy,
You're "the winner" of 'the lottery'!
Yours sincerly,
Dave
"AAABBBB\"
斜线字符串
使用/
作为分隔符,Slashy字符串对于定义正则表达式和模式特别有用,因为没有必要转义反斜杠。
前斜线与单引号:
def fooPattern = /.*foo.*/
assert fooPattern == '.*foo.*'
若在斜线字符串中要使用前斜线,需要用反斜线转义
def escapeSlash = /The character \/ is a forward \t slash //用反斜线转义前斜线,而反斜线可直接写入,适用于正则式
assert escapeSlash == 'The character / is a forward \\t slash' //单引号等其他字符串前斜线不需要转义
可表示多行字符串
def multilineSlashy = /one\
two
three/
assert multilineSlashy.contains('\n')
println(multilineSlashy)
运行结果:
one two
three
支持插入占位符
def color = "blue"
def interpolatedSlashy = /a ${color} car/
assert interpolatedSlashy == /a blue car/
println(interpolatedSlashy)
需要注意的问题:
一个空的slashy字符串不能用双斜杠表示,因为它被Groovy解析器理解为一行注释,下面的断言实际上不会被编译,因为它被认为是一个未结束语句:
def color = ''
assert color == //
由于slashy字符串的设计主要是为了使regexp更容易,因此在GStrings中有一些错误,比如$()
,在slashy字符串中可以使用
def slashy = /a $() car/
assert slashy == "a \$() car" // GString中$需要转义