1.注释
1.1 单行注释
单行注释开头为//
,可以在行中的任何位置。从//
开始,直到该行的末端,被视为注释的一部分。
// a standalone single line commentprintln "hello" // a comment till the end of the line
1.2 多行注释
多行注释以行开头/*
,可以在行中的任何位置找到。以下字符/*
将被视为注释的一部分,包括换行符,直到第一个*/
关闭注释。多行注释因此可以放在语句的末尾,甚至在语句内。
/* a standalone multiline comment spanning two lines */println "hello" /* a multiline comment starting at the end of a statement */println 1 /* one */ + 2 /* two */
1.3 GroovyDoc注释
与多行注释类似,GroovyDoc注释是多行的,但以开头/**
和结尾*/
。第一个GroovyDoc注释行后面的行可以选择以星号开头*
。这种注释一般用于:
-
类型定义(类,接口,枚举,注释)
-
字段和属性定义
-
方法定义
/** * A Class description */class Person { /** the name of the person */ String name /** * Creates a greeting method for a certain person. * * @param otherPerson the person to greet * @return a greeting message */ String greet(String otherPerson) { "Hello ${otherPerson}" }}
GroovyDoc遵循与Java自己的JavaDoc相同的约定。因此,您将能够使用与JavaDoc相同的标签。
1.4 shebang行
除了单行注释,还有一个特殊的行注释,通常称为UNIX系统理解的shebang行,允许直接从命令行运行脚本,前提是已安装了Groovy发行版并且groovy
命令已经配置到系统PATH
。
#!/usr/bin/env groovyprintln "Hello from the shebang line"
该# 字符必须是文件的第一个字符。任何缩进都会导致编译错误。 |
2.关键字(Keywords)
以下列表代表Groovy语言的所有关键字:
表1.关键字
as | assert | break | case |
catch | class | const | continue |
def | default | do | else |
enum | extends | false | finally |
for | goto | if | implements |
import | in | instanceof | interface |
new | null | package | return |
super | switch | this | throw |
throws | trait | true | try |
while |
3 标识符(类名、变量名以及方法名)
3.1 一般标识符
标识符以字母,$或下划线开头。他们不能以数字开头。
字母可以在以下范围内:
-
'a'到'z'(小写ASCII字母)
-
'A'到'Z'(大写ASCII字母)
-
'\ u00C0'至'\ u00D6'
-
'\ u00D8'至'\ u00F6'
-
'\ u00F8'至'\ u00FF'
-
'\ u0100'至'\ uFFFE'
以下是一些有效标识符(这里是变量名)的示例:
def namedef item3def with_underscoredef $dollarStart
但以下是无效的标识符:
def 3tierdef a+bdef a#b
以点号分隔标示符也是有效的:
foo.asfoo.assertfoo.breakfoo.casefoo.catch
3.2 带引号的标识符
带引号的标识符显示在点表达式的点后面。例如,表达式的name
部分person.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"assert map.'with-dash-signs-and-single-quotes' == "ALLOWED"
groovy的字符串有很多种类型,基本上所用类型的字符串都可以在点表达式的点后面:
map.'single quote'map."double quote"map.'''triple single quote'''map."""triple double quote"""map./slashy string/map.$/dollar slashy string/$
在纯字符串和Groovy的GStrings(插值字符串)之间有一个区别,因为在后一种情况下,内插值插入到final字符串中以计算整个标识符:
def firstname = "Homer"map."Simson-${firstname}" = "Homer Simson"assert map.'Simson-Homer' == "Homer Simson"
4 字符串
Groovy通过实例化java.lang.String
对象或GStrings (groovy.lang.GString
)来创建和操作字符串。
4.1 单引号字符串
单引号字符串是由单引号包围的一系列字符:
'a single quoted string'
单引号字符串是纯的java.lang.String ,不支持插值。 |
4.2 字符串连接
所有Groovy字符串可以与+
运算符连接:
assert 'ab' == 'a' + 'b'
4.3 三重单引号字符串
三重单引号字符串是由三个单引号包围的一系列字符:
'''a triple single quoted string'''
三重单引号字符串是纯的java.lang.String ,不支持插值。 |
三重单引号中的字符串可以分多行来写,无需连接运算符(+)连接和转义:
def aMultilineString = '''line oneline twoline three'''
4.3.1 转义特殊字符
您可以使用反斜杠字符转义单引号:
'an escaped single quote: \' needs a backslash'
你可以用双反斜杠转义转义字符本身:
'an escaped escape character: \\ needs a double backslash'
一些特殊字符也使用反斜杠作为转义字符:
转义字符 | 表示 |
---|---|
'\ t' | 制表 |
'\ b' | 退格 |
'\ n' | 换行 |
'\ r' | 回车 |
'\F' | 换页 |
'\\' | 反斜杠 |
'\'' | 单引号(单引号和三单引号字符串) |
'\“' | 双引号(对于双引号和三双引号字符串) |
4.3.2 Unicode转义序列
对于键盘上不存在的字符,可以使用unicode转义序列:反斜杠,后跟'u',然后是4个十六进制数字。
例如,欧元货币符号可以表示为:
'The Euro currency symbol: \u20AC'
4.4 双引号字符串
双引号字符串是由双引号包围的一系列字符:
"a double quoted string"
4.4.1 字符串插值
在双引号字符串中,我们可以插入一个变量占位符,当字符串输出时,占位符显示的是变量的值:
def name = 'Guillaume' // a plain stringdef greeting = "Hello ${name}"assert greeting.toString() == 'Hello Guillaume'
我们还可以在双引号字符串中插入表达式,在字符串输出时,显示表达式的值:
def sum = "The sum of 2 and 3 equals ${2 + 3}"assert sum.toString() == 'The sum of 2 and 3 equals 5'
除了${}
占位符之外,还可以去掉{}而直接使用$:
def person = [name: 'Guillaume', age: 36]assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'
如果使用$的形式求值,而非${}形式,要注意$后的以点号串起来的内容一直到遇到非法字符或字符串末尾都是一个变量标示符:
def number = 3.14
以下语句将抛出一个错误groovy.lang.MissingPropertyException
:
shouldFail(MissingPropertyException) { println "$number.toString()"}
原因就是:解析器把"$number.toString()"
解释为"${number.toString}()"
。
如果你需要在GString 中转义$
或${}
占位符,你只需要使用\
反斜杠字符来转义$符号:
assert '${name}' == "\${name}"
4.4.2 内插闭包表达式的特殊情况
到目前为止,我们已经看到我们可以在占位符内插入${}
任意表达式,但是有一个特殊的情况——插入闭包表达式。当占位符包含一个箭头时${->}
,表示在字符串中插入闭包表达式:
def sParameterLessClosure = "1 + 2 == ${-> 3}" assert sParameterLessClosure == '1 + 2 == 3'def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" assert sOneParamClosure == '1 + 2 == 3'
下面的例子来说明内插闭包表达式相对于内插普通表达式的优势:惰性求值。
def number = 1 def eagerGString = "value == ${number}"def lazyGString = "value == ${ -> number }"assert eagerGString == "value == 1" assert lazyGString == "value == 1" number = 2 assert eagerGString == "value == 1" assert lazyGString == "value == 2"
定义一个number
变量赋值为1,然后在eagerGString
作为一个普通表达式插入,在lazyGString
作为一个闭包表达式插入,通过验证可知两个变量的结果相同;接下来我们改变number
的值为2,验证发现eagerGString
的值不变,lazyGString
的值变为2。
4.4.3 GString和String的转化
如果一个方法(无论是在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 def result = takeString(message) assert result instanceof Stringassert result == 'The message is hello'
4.4.4 GString和String hashCodes
String和GString的hashCodes是不同的,前者的hashCodes是不可变的,而后者的hashCodes取决于它的内插值,即时是相同的字符串结果,两者的hashCodes也不相同。
assert "one: ${1}".hashCode() != "one: 1".hashCode()
不要使用GString作为Map键,要用String作为键来检索。
def key = "a"def m = ["${key}": "letter ${key}"] assert m["a"] == null
4.5 三重双引号字符串
三重双引号字符串的特性同双引号字符串,只不过它们可以是多行的,像三重单引号的字符串。
def name = 'Groovy'def template = """ Dear Mr ${name}, You're the winner of the lottery! Yours sincerly, Dave"""assert template.toString().contains('Groovy')
双引号和单引号都不需要在三重双引号字符串中转义。 |
4.6 斜线字符串
除了通常引用的字符串,Groovy提供了斜线字符串,/
用作分隔符。斜线字符串对定义正则表达式和模式特别有用,因为不需要反斜杠,是表达式看上去简洁。
斜线字符串示例:
def fooPattern = /.*foo.*/assert fooPattern == '.*foo.*'
内部的正斜杠需要用反斜杠转义:
def escapeSlash = /The character \/ is a forward slash/assert escapeSlash == 'The character / is a forward slash'
斜线字符串可以是多行的:
def multilineSlashy = /one two three/assert multilineSlashy.contains('\n')
斜线字符串也可以插值(即GString,这个特性同双引号字符串):
def color = 'blue'def interpolatedSlashy = /a ${color} car/assert interpolatedSlashy == 'a blue car'
空的字符串不能用双正斜杠表示,因为Groovy解析器将其理解为行注释:
assert '' == //
4.7 $/.../$字符串
$/.../$字符串是多行GStrings与一个开始$/
和一个结束/$
组成的。要注意的是,它用来转义的字符是$,而不是反斜线。
看如下例子:
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 dollar slashy string delimiter/$assert [ 'Guillaume', 'April, 1st', '$ dollar sign', '$ escaped dollar sign', '\\ backslash', '/ forward slash', '$/ escaped forward slash', '/$ escaped dollar slashy string delimiter' ].each { dollarSlashy.contains(it) }
4.8 字符串汇总表
字符串名称 | 字符串语法 | 插值 | 多行 | 用来转义的字符 |
单引号 |
|
|
|
|
三重单引号 |
|
| 是 |
|
双引号 |
| 是 |
|
|
三重双引号 |
| 是 | 是 |
|
双斜线 |
| 是 | 是 |
|
$/.../$ |
| 是 | 是 |
|
4.9 字符
Groovy可以通过以下3种方式将字符串变为字符串:
char c1 = 'A' assert c1 instanceof Characterdef c2 = 'B' as char assert c2 instanceof Characterdef c3 = (char)'C' assert c3 instanceof Character
5 数字类型
5.1 整数类型
整数文本类型与Java中的相同:
-
byte
-
char
-
short
-
int
-
long
-
java.lang.BigInteger
使用具体类型声明:
// primitive typesbyte b = 1char c = 2short s = 3int i = 4long l = 5// infinite precisionBigInteger bi = 6
使用def
关键字声明创建,将有所不同:Groovy会根据数值大小自动适配类型。
正数情况:
def a = 1assert a instanceof Integer// Integer.MAX_VALUEdef b = 2147483647assert b instanceof Integer// Integer.MAX_VALUE + 1def c = 2147483648assert c instanceof Long// Long.MAX_VALUEdef d = 9223372036854775807assert d instanceof Long// Long.MAX_VALUE + 1def e = 9223372036854775808assert e instanceof BigInteger
负数情况:
def na = -1assert na instanceof Integer// Integer.MIN_VALUEdef nb = -2147483648assert nb instanceof Integer// Integer.MIN_VALUE - 1def nc = -2147483649assert nc instanceof Long// Long.MIN_VALUEdef nd = -9223372036854775808assert nd instanceof Long// Long.MIN_VALUE - 1def ne = -9223372036854775809assert ne instanceof BigInteger
5.1.1 非十进制表示
数字也可以用二进制,八进制,十六进制和十进制表示。
二进制数字以0b
前缀开头:
int xInt = 0b10101111assert xInt == 175short xShort = 0b11001001assert xShort == 201 as shortbyte xByte = 0b11assert xByte == 3 as bytelong xLong = 0b101101101101assert xLong == 2925lBigInteger xBigInteger = 0b111100100001assert xBigInteger == 3873gint xNegativeInt = -0b10101111assert xNegativeInt == -175
八进制以0
前缀开头:
int xInt = 077assert xInt == 63short xShort = 011assert xShort == 9 as shortbyte xByte = 032assert xByte == 26 as bytelong xLong = 0246assert xLong == 166lBigInteger xBigInteger = 01111assert xBigInteger == 585gint xNegativeInt = -077assert xNegativeInt == -63
十六进制文字
十六进制以0x
前缀开头:
int xInt = 0x77assert xInt == 119short xShort = 0xaaassert xShort == 170 as shortbyte xByte = 0x3aassert xByte == 58 as bytelong xLong = 0xffffassert xLong == 65535lBigInteger xBigInteger = 0xaaaaassert xBigInteger == 43690gDouble xDouble = new Double('0x1.0p0')assert xDouble == 1.0dint xNegativeInt = -0x77assert xNegativeInt == -119
5.2 小数类型
-
float
-
double
-
java.lang.BigDecimal
十进制表示方法:
// primitive typesfloat f = 1.234double d = 2.345// infinite precisionBigDecimal bd = 3.456
也可以用指数表示法,带有e
或E
指数字母,后跟可选符号和表示指数的整数:
assert 1e3 == 1_000.0assert 2E4 == 20_000.0assert 3e+1 == 30.0assert 4E-2 == 0.04assert 5e-1 == 0.5
为了精确小数计算,Groovy def定义的数据类型默认是java.lang.BigDecimal
的。
小数不能使用二进制,八进制或十六进制表示形式表示。 |
5.3 分组下划线
Groovy允许对使用下滑线对数字分组,对于较长的数组比较有用:
long creditCardNumber = 1234_5678_9012_3456Llong socialSecurityNumbers = 999_99_9999Ldouble monetaryAmount = 12_345_132.12long hexBytes = 0xFF_EC_DE_5Elong hexWords = 0xFFEC_DE5Elong maxLong = 0x7fff_ffff_ffff_ffffLlong alsoMaxLong = 9_223_372_036_854_775_807Llong bytes = 0b11010010_01101001_10010100_10010010
5.4 数字类型后缀
我们可以通过后缀来指定数字类型(大小写不区分)。
类型 | 后缀 |
---|---|
BigInteger |
|
Long |
|
Integer |
|
BigDecimal |
|
Double |
|
Float |
|
例子:
assert 42I == new Integer('42')assert 42i == new Integer('42') // lowercase i more readableassert 123L == new Long("123") // uppercase L more readableassert 2147483648 == new Long('2147483648') // Long type used, value too large for an Integerassert 456G == new BigInteger('456')assert 456g == new BigInteger('456')assert 123.45 == new BigDecimal('123.45') // default BigDecimal type usedassert 1.200065D == new Double('1.200065')assert 1.234F == new Float('1.234')assert 1.23E23D == new Double('1.23E23')assert 0b1111L.class == Long // binaryassert 0xFFi.class == Integer // hexadecimalassert 034G.class == BigInteger // octal
5.5 数学运算符
下面来看一下二元运算类型规则,这里不包括除法运算和幂运算:
-
byte
,char
,short
和int
之间,结果为int
-
long
与byte
,char
,short
,int
中任意一个,结果为long
-
BigInteger
和任何其他积分类型的结果BigInteger
-
BigDecimal
和byte
,char
,short
,int
,BigInteger
中一个,结果为BigDecimal
-
float
,double
和BigDecimal
,结果为double
-
两个
BigDecimal
,结果BigDecimal
下表总结了这些规则:
byte | char | short | int | long | BigInteger | float | double | BigDecimal | |
---|---|---|---|---|---|---|---|---|---|
byte | int | int | int | int | long | BigInteger | double | double | BigDecimal |
char | int | int | int | long | BigInteger | double | double | BigDecimal | |
short | int | int | long | BigInteger | double | double | BigDecimal | ||
int | int | long | BigInteger | double | double | BigDecimal | |||
long | long | BigInteger | double | double | BigDecimal | ||||
BigInteger | BigInteger | double | double | BigDecimal | |||||
float | double | double | double | ||||||
double | double | double | |||||||
BigDecimal | BigDecimal |
5.5.1 除法运算
- 除法运算中两个操作数中,其中一个如果为
float
、double
,则结果是double
类型的。 - 当两个操作数都是整型(
short
、char
、byte
、int
、long
或BigInteger
)或者BigDecimal
时,结果是BigDecimal
。 - 如果要像Java那样取整,需要调用
intdiv
方法。
5.5.2 幂运算
幂运算由**
运算符表示,具有两个参数:基数和指数。幂运算的结果类型取决于其操作数和操作的结果(特别是如果结果可以表示为整数值)。
Groovy的幂运算使用以下规则来确定结果类型:
-
如果指数是十进制值
-
if结果可以表示为一个
Integer
,那么返回一个Integer
-
else if结果可以表示为
Long
,那么返回Long
-
else返回
Double
-
-
如果指数是整数值
-
如果指数负,则返回
Integer
,Long
或者Double
如果结果值适合该类型 -
如果指数为正或零
-
如果基数是
BigDecimal
,则返回一个BigDecimal
结果值 -
如果基数是
BigInteger
,则返回一个BigInteger
结果值 -
如果基数是一个
Integer
,如果结果值适合它则返回一个Integer
,否则BigInteger
-
如果基数是
Long
,如果结果值适合它则返回Long
,否则BigInteger
-
-
我们可以用几个例子来说明这些规则:
// base and exponent are ints and the result can be represented by an Integerassert 2 ** 3 instanceof Integer // 8assert 10 ** 9 instanceof Integer // 1_000_000_000// the base is a long, so fit the result in a Long// (although it could have fit in an Integer)assert 5L ** 2 instanceof Long // 25// the result can't be represented as an Integer or Long, so return a BigIntegerassert 100 ** 10 instanceof BigInteger // 10e20assert 1234 ** 123 instanceof BigInteger // 170515806212727042875...// the base is a BigDecimal and the exponent a negative int// but the result can be represented as an Integerassert 0.5 ** -2 instanceof Integer // 4// the base is an int, and the exponent a negative float// but again, the result can be represented as an Integerassert 1 ** -0.3f instanceof Integer // 1// the base is an int, and the exponent a negative int// but the result will be calculated as a Double// (both base and exponent are actually converted to doubles)assert 10 ** -1 instanceof Double // 0.1// the base is a BigDecimal, and the exponent is an int, so return a BigDecimalassert 1.2 ** 10 instanceof BigDecimal // 6.1917364224// the base is a float or double, and the exponent is an int// but the result can only be represented as a Double valueassert 3.4f ** 5 instanceof Double // 454.35430372146965assert 5.6d ** 2 instanceof Double // 31.359999999999996// the exponent is a decimal value// and the result can only be represented as a Double valueassert 7.8 ** 1.9 instanceof Double // 49.542708423868476assert 2 ** 0.1f instanceof Double // 1.0717734636432956
6 布尔类型
Boolean是用于表示真值的特殊数据类型:true
和false
。
布尔值可以存储在变量中,分配给字段,就像任何其他数据类型:
def myBooleanVariable = trueboolean untypedBooleanVar = falsebooleanField = true
true
和false
是唯一的两个原始布尔值。但是更复杂的布尔表达式可以使用符来。
此外,Groovy有(通常称为Groovy Truth)用于将非布尔对象强制转换为布尔值。
7 Lists
Groovy使用逗号分隔的值列表,用方括号括起来表示List。Groovy List是普通的JDK java.util.List
,Groovy没有定义自己的集合类,只是扩展了JDK的集合类,使它们用起来更简洁方便。
def numbers = [1, 2, 3] assert numbers instanceof List assert numbers.size() == 3
在上面的例子中,我们创建了一个同类型值列表,你也可以创建包含不同类型值的列表:
def heterogeneous = [1, "a", true]
这里的列表包含一个数字,一个字符串和一个布尔值 |
默认情况下,Groovy声明的列表引用是 java.util.ArrayList
类型的,可以通过as
运算符或显示声明改为其他类型:
def arrayList = [1, 2, 3]assert arrayList instanceof java.util.ArrayListdef linkedList = [2, 3, 4] as LinkedList assert linkedList instanceof java.util.LinkedListLinkedList otherLinked = [3, 4, 5] assert otherLinked instanceof java.util.LinkedList
可以使用[]
带正索引或负索引的下标运算符访问(读取和设置值)列表中的元素,如果下表是正值则访问是从左向右,如果为负值访问则是从右向左的;可以用<<将元素添加到列表中:
def letters = ['a', 'b', 'c', 'd']assert letters[0] == 'a' assert letters[1] == 'b'assert letters[-1] == 'd' assert letters[-2] == 'c'letters[2] = 'C' assert letters[2] == 'C'letters << 'e' assert letters[ 4] == 'e'assert letters[-1] == 'e'assert letters[1, 3] == ['b', 'd'] assert letters[2..4] == ['C', 'd', 'e']
列表还可以包含其他列表以创建多维列表:
def multi = [[0, 1], [2, 3]] assert multi[1][0] == 2
8 数组
Groovy数组的定义有两种方式,一种是显示声明创建,另一种是用as
运算符:
String[] arrStr = ['Ananas', 'Banana', 'Kiwi'] assert arrStr instanceof String[] assert !(arrStr instanceof List)def numArr = [1, 2, 3] as int[] assert numArr instanceof int[] assert numArr.size() == 3
可以创建多维数组:
def matrix3 = new Integer[3][3] assert matrix3.size() == 3Integer[][] matrix2 matrix2 = [[1, 2], [3, 4]]assert matrix2 instanceof Integer[][]
数组元素的访问和列表访问方式是相同的:
String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']assert names[0] == 'Cédric' names[2] = 'Blackdrag' assert names[2] == 'Blackdrag'
Groovy不支持Java的数组初始化符号,因为花括号可能被错误解释为Groovy闭包的符号。 |
9 MAP
某些语言称为字典或关联数组,Groovy叫映射。将键和值用冒号分隔,键/值对之间使用逗号分隔,然后将所有键和值对放到方括号里:
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF'] assert colors['red'] == '#FF0000' assert colors.green == '#00FF00' colors['pink'] = '#FF00FF' colors.yellow = '#FFFF00' assert colors.pink == '#FF00FF'assert colors['yellow'] == '#FFFF00'assert colors instanceof java.util.LinkedHashMap
如果你检索映射中不存在的键,结果为null:
assert colors.unknown == null
在上面的示例中,使用字符串键,也可以使用其他类型的值作为键:
def numbers = [1: 'one', 2: 'two']assert numbers[1] == 'one'
如果你想把变量或表达式作为映射中的键,需要把把变量和表达式放到小括号里:
person = [(key): 'Guillaume'] assert person.containsKey('name') assert !person.containsKey('key')