作者:杨龙,叩丁狼教育
本文参考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')
特殊转移符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) }
字符串汇总
字符
与 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
数值类型强制表示
使用后缀强制一个数值的类型:
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
计算结果类型判定
- 运算结果类型取决于表达中类型数值范围最大的,但任何与 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')
,