作者:杨龙,叩丁狼教育
本文参考groovy手册,对Groovy的基础语法做一个快速介绍。
注释
单行注释
// a standalone single line comment
println "hello" // a comment till the end of the line
多行注释
/* a standalone multiline comment
spanning two lines */
println "hello" /* a multiline comment starting
at the end of a statement */
println 1 /* one */ + 2 /* two */
Groovy文档注释
GroovyDoc 遵循与 Java 自己的 JavaDoc 相同的约定,可以使用与 JavaDoc 相同的标签。
/**
* 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}"
}
}
Groovy中的关键字
- 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
标识符
普通标识符
- 标识符以字母,美元或下划线开头,不能以数字开头,接下来的字符可以包含字母和数字。
- 所有关键字在跟随
.
也是有效的标识符。
// 正确
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
引号标识符
引号标识符出现在表达式 .
后面。 例如,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"
map.'single quote'
map."double quote"
map.'''triple single quote'''
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$
def firstname = "Homer"
map."Simson-${firstname}" = "Homer Simson" // 支持插值
assert map.'Simson-Homer' == "Homer Simson"
字符串
- java.lang.String // 不支持插值
- GStrings (groovy.lang.GString) // 支持插值
单引号字符串
单引号字符串为 java.lang.String,不支持插值。
'ab' == 'a' + 'b' // 字符串拼接
三重单引号字符串
三重单引号字符串为 java.lang.String,不支持插值,三重单引号字符串是多行的,不要用换行符来换行。
def startingAndEndingWithANewline = '''
line one
line two
line three
'''
def strippedFirstNewline = '''\ // 字符串包含换行符作为第一个字符,可以通过用反斜杠转义换行来剥离该字符?
line one
line two
line three
'''
assert !strippedFirstNewline.startsWith('\n')
特殊转移符
Escape | sequence Character |
---|---|
'\t' | tabulation |
'\b' | backspace |
'\n' | newline |
'\r' | carriage return |
'\f' | formfeed |
'\' | backslash |
''' | single quote (for single quoted and triple single quoted strings) |
'"' | double quote (for double quoted and triple double quoted strings) |
Unicode形式转义符
用于键盘上不存在的字符。
'The Euro currency symbol: \u20AC'
双引号字符串
- 若没有插值表达式(interpolated expression),则双引号字符串为 java.lang.String,但若存在插值表达式,则为 groovy.lang.GString。
- 除了单引号和三引号字符串外,任何字符串支持插值。
- 插值是在对字符串进行评估时用其值替换字符串中的占位符的行为,占位符表达式由
${}
。 -
${}
占位符之间不仅允许表达式,语句也被允许。但语句的值只是null
,因此如果在该占位符中插入了几个语句,最后一个应该以某种方式返回一个有意义的值(不推荐插入语句)。 - 在
.
表达式使用$
占位符,但表达式中若有方法调用,闭包的花括号或算术运算符将无效。 - 若在
$
使用转义符号\
,则不有会插值效果。
以下
def name = 'Guillaume' // a plain string
def 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 a = 1; def b = 2; a + b}
def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'
def number = 3.14
shouldFail(MissingPropertyException) {
println "$number.toString()" // 语句将抛出一个 groovy.lang.MissingPropertyException,因为 Groovy 认为您正在尝试访问该数字的 toString 属性,该属性不存在,可以理解 Groovy 解析成 "${number.toString}()"。
}
三重双引号字符串
三重双引号字符串表现和双引号字符串一样,另外它是多行的。
def name = 'Groovy'
def template = """
Dear Mr ${name},
You're the winner of the lottery!
Yours sincerly,
Dave
"""
assert template.toString().contains('Groovy')
反斜线字符串
- 使用
/
作为分隔符的斜线字符串。 Slashy 字符串对于定义 正则表达式 特别有用,因为不需要转义反斜杠。 - 也是多行也能被插值。
- 一个空的斜线字符串不能用双正斜杠表示,因为 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')
def color = 'blue'
def interpolatedSlashy = /a ${color} car/
assert interpolatedSlashy == 'a blue car'
美元转义符
- Dollar slashy string 是多行 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) }
字符串汇总
String name | String syntax | Interpolated | Multiline | Escape character |
---|---|---|---|---|
Single quoted | '…' | \ | ||
Triple single quoted | '''…''' | true | \ | |
Double quoted | "…" | true | \ | |
Triple double quoted | """…""" | true | true | \ |
Slashy | /…/ | true | true | \ |
Dollar slashy | true | true | $ |
字符
与 Java 不同,Groovy 没有明确的字符字面量。 但可以通过三种不同的方式明确地将 Groovy 字符串设置成字符类型。
char c1 = 'A'
assert c1 instanceof Character
def c2 = 'B' as char
assert c2 instanceof Character
def c3 = (char)'C'
assert c3 instanceof Character
// 当字符被保存在一个变量中时,使用第一种方式好,而当一个 char 值必须作为方法调用的参数传递时使用另外两种方式好。
数字常量
支持的常量类型
- byte
- char
- short
- int
- long
- java.lang.BigInteger
byte b = 1
char c = 2
short s = 3
int i = 4
long l = 5
// infinite precision
BigInteger bi = 6
若使用 def
关键字声明,则类型取决于该值所兼容的类型。
def a = 1
assert a instanceof Integer
// Integer.MAX_VALUE
def b = 2147483647
assert b instanceof Integer
// Integer.MAX_VALUE + 1
def c = 2147483648
assert c instanceof Long
// Long.MAX_VALUE
def d = 9223372036854775807
assert d instanceof Long
// Long.MAX_VALUE + 1
def e = 9223372036854775808
assert e instanceof BigInteger
def na = -1
assert na instanceof Integer
// Integer.MIN_VALUE
def nb = -2147483648
assert nb instanceof Integer
// Integer.MIN_VALUE - 1
def nc = -2147483649
assert nc instanceof Long
// Long.MIN_VALUE
def nd = -9223372036854775808
assert nd instanceof Long
// Long.MIN_VALUE - 1
def ne = -9223372036854775809
assert ne instanceof BigInteger
非十进制表示法
二进制表示
二进制数字以 0b
前缀开头:
int xInt = 0b10101111
assert xInt == 175
short xShort = 0b11001001
assert xShort == 201 as short
byte xByte = 0b11
assert xByte == 3 as byte
long xLong = 0b101101101101
assert xLong == 2925l
BigInteger xBigInteger = 0b111100100001
assert xBigInteger == 3873g
int xNegativeInt = -0b10101111
assert xNegativeInt == -175
八进制表示
八进制数字以 0
前缀开头:
int xInt = 077
assert xInt == 63
short xShort = 011
assert xShort == 9 as short
byte xByte = 032
assert xByte == 26 as byte
long xLong = 0246
assert xLong == 166l
BigInteger xBigInteger = 01111
assert xBigInteger == 585g
int xNegativeInt = -077
assert xNegativeInt == -63
十六进制表示
十六进制数字以 0x
前缀开头:
int xInt = 0x77
assert xInt == 119
short xShort = 0xaa
assert xShort == 170 as short
byte xByte = 0x3a
assert xByte == 58 as byte
long xLong = 0xffff
assert xLong == 65535l
BigInteger xBigInteger = 0xaaaa
assert xBigInteger == 43690g
Double xDouble = new Double('0x1.0p0')
assert xDouble == 1.0d
int xNegativeInt = -0x77
assert xNegativeInt == -119
小数
float
double
java.lang.BigDecimal
Groovy 使用
BigDecimal
是小数的默认值。 另外,float
和double
都支持,但需要一个明确的类型声明、类型强制或后缀。在方法或闭包中接受一个类型
BigDecimal
类型的值,即使方法参数类型是float
和double
。不能使用二进制,八进制或十六进制表示来表示小数。
// primitive types
float f = 1.234
double d = 2.345
// infinite precision
BigDecimal bd = 3.456
assert 1e3 == 1_000.0
assert 2E4 == 20_000.0
assert 3e+1 == 30.0
assert 4E-2 == 0.04
assert 5e-1 == 0.5
下划线在数字常量中的使用
long creditCardNumber = 1234_5678_9012_3456L
long socialSecurityNumbers = 999_99_9999L
double monetaryAmount = 12_345_132.12
long hexBytes = 0xFF_EC_DE_5E
long hexWords = 0xFFEC_DE5E
long maxLong = 0x7fff_ffff_ffff_ffffL
long alsoMaxLong = 9_223_372_036_854_775_807L
long bytes = 0b11010010_01101001_10010100_10010010
数值类型强制表示
使用后缀强制一个数值的类型:
Type | Suffix |
---|---|
BigInteger | G or g |
Long | L or l |
Integer | I or i |
BigDecimal | G or g |
Double | D or d |
Float | F or f |
assert 42I == new Integer('42')
assert 42i == new Integer('42') // lowercase i more readable
assert 123L == new Long("123") // uppercase L more readable
assert 2147483648 == new Long('2147483648') // Long type used, value too large for an Integer
assert 456G == new BigInteger('456')
assert 456g == new BigInteger('456')
assert 123.45 == new BigDecimal('123.45') // default BigDecimal type used
assert 1.200065D == new Double('1.200065')
assert 1.234F == new Float('1.234')
assert 1.23E23D == new Double('1.23E23')
assert 0b1111L.class == Long // binary
assert 0xFFi.class == Integer // hexadecimal
assert 034G.class == BigInteger // octal
计算结果类型判定
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 |
- 运算结果类型取决于表达中类型数值范围最大的,但任何与
float
及double
运算结果都是double
。
除法操作
- 正数相除可以使用
/
或方法 intdiv()。 - BigDecimal 类型相除使用方法 divide。
def a = 2.intdiv(2) // 1
def b = 2.0.divide(1.0) // 1
def c = 2.000000.divide(1.00, 1, RoundingMode.HALF_UP) // 2.0
幂运算
幂运算符由 **
运算符表示,其中有两个参数:基数和指数。 幂运算符的结果取决于其操作数和操作结果。
-
指数是小数
- 若结果可以为
Integer
类型,则返回是Integer
类型值。 - 若结果可以为
Long
类型,则返回Long
类型值。 - 其它情况返回
Double
类型值。
- 若结果可以为
-
指数是正数
- 负数
- 若结果值适合该类型,则返回
Integer
、Long
或Double
类型值。
- 若结果值适合该类型,则返回
- 正数或 0
- 若基数是
BigDecimal
类型,则返回是BigDecimal
类型值。 - 若基数是
BigInteger
类型,则返回是BigInteger
类型值。 - 若基数是
Integer
类型,且结果值适合该类型,则返回Integer
或BigInteger
类型值。 - 若基数是
Long
类型,且结果值适合该类型,则返回Long
或BigInteger
类型值。
- 若基数是
- 负数
// base and exponent are ints and the result can be represented by an Integer
assert 2 ** 3 instanceof Integer // 8
assert 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 BigInteger
assert 100 ** 10 instanceof BigInteger // 10e20
assert 1234 ** 123 instanceof BigInteger // 170515806212727042875...
// the base is a BigDecimal and the exponent a negative int
// but the result can be represented as an Integer
assert 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 Integer
assert 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 BigDecimal
assert 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 value
assert 3.4f ** 5 instanceof Double // 454.35430372146965
assert 5.6d ** 2 instanceof Double // 31.359999999999996
// the exponent is a decimal value
// and the result can only be represented as a Double value
assert 7.8 ** 1.9 instanceof Double // 49.542708423868476
assert 2 ** 0.1f instanceof Double // 1.0717734636432956
布尔
-
true
和false
是唯一的两个原始布尔值。 - Groovy 有一个特殊的规则(通常称为Groovy Truth),用于将非布尔对象强制为一个布尔值。
def myBooleanVariable = true
boolean untypedBooleanVar = false
booleanField = true
列表
- Groovy 使用
,
分隔的值,用[]
括起来表示 lists。 - 因为 Groovy 没有定义自己的集合类,Groovy lists 是 JDK java.util.List,默认使用的实现类是 java.util.ArrayList,除非手动通过操作符
as
和声明类型指定。 - lists 可以存放多种类型元素。
def numbers = [1, 2, 3]
assert numbers instanceof List
assert numbers.size() == 3
def heterogeneous = [1, "a", true]
def arrayList = [1, 2, 3]
assert arrayList instanceof java.util.ArrayList
def linkedList = [2, 3, 4] as LinkedList
assert linkedList instanceof java.util.LinkedList
LinkedList otherLinked = [3, 4, 5]
assert otherLinked instanceof java.util.LinkedList
- 可通过索引读取和设置值访问 list 中的元素,可用
<<
leftShift 运算符将元素附加到 list 中。 - 索引范围的值:[-list.size(), list.size() - 1],小于最小索引获取元素报索引越界,大于最大索引获取元素是
null
(这点不同 Java)。 - list 元素可以是另一个 list。
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' // 添加元素到 list 尾部。
assert letters[4] == 'e'
assert letters[-1] == 'e' // 注意这个 索引 -1 上的值是最后一个元素
assert letters[1, 3] == ['b', 'd'] // 注意这个返回一个新的 list
assert letters[2..4] == ['C', 'd', 'e'] // 注意这个返回一个新的 list
def multi = [[0, 1], [2, 3]]
assert multi[1][0] == 2
数组
- 定义数组 Groovy 重用 list 定义方式,若要明确数组类型可通过
as
强制或类型声明。
String[] arrStr = ['Ananas', 'Banana', 'Kiwi'] // 类型声明
assert arrStr instanceof String[]
assert !(arrStr instanceof List)
def numArr = [1, 2, 3] as int[] // `as` 强制
assert numArr instanceof int[]
assert numArr.size() == 3
- 支持定义多维数组。
- Groovy 不支持 Java 数组初始化符号
{}
,因为{}
可能会被曲解成 Groovy 闭包的符号。
def matrix3 = new Integer[3][3]
assert matrix3.size() == 3
Integer[][] 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'
Maps
- Groovy 具有 maps 关联键映射到值,使用冒号分隔键和值,并使用逗号分隔每个键/值对,以及由方括号包围的整个键和值。
- Groovy 创建的 map 使用的是 java.util.LinkedHashMap 的实例。
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
assert colors.unknown == null // 不存在 key 返回 null
- 可以使用其他类型值作为键。
def numbers = [1: 'one', 2: 'two']
assert numbers[1] == 'one'
def key = 'name'
def persons = [key: 'Guillaume']
assert !persons.containsKey('name')
assert persons.containsKey('key')
person = [(key): 'Guillaume']
assert person.containsKey('name')
assert !person.containsKey('key')
- 若变量或表达式作为 map 中的键使用,必须用括号括住变量或表达式
def key = 'name'
def persons = [(key): 'Guillaume']
assert persons.containsKey('name')
assert !persons.containsKey('key')