Gradle基础之Grovy语法

Gradle基础之Groovy语法

在使用Android Studio开发app的过程中,都会接触到Gradle配置,而Gradle的配置是基于Grovvy语法的。因此,要想熟练的使用Gradle进行配置,就必须熟悉Groovy语法,下面开始讲解下Groovy的基本语法。

Groovy可以看作是java的加强版,扩展了java的语法,拥有自己的一些特性。

Groovy语法官方文档

注释

单行注释

单行注释如下所示,跟java一样:

1
2
// a standalone single line comment
println "hello" // a comment till the end of the line

多行注释

多行注释如下所示,跟java一样:

1
2
3
4
5
/* a standalone multiline comment
spanning two lines */
println "hello" /* a multiline comment starting
at the end of a statement */
println 1 /* one */ + 2 /* two */

GroovyDoc 注释

GroovyDoc跟javaDoc语法也是一样的,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 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}"
}
}

Shebang line

除了单行注释,还有一种特殊的单行注释,如下所示,叫做Shebang line(各位可以自行去翻译)。这行注释主要是便于unix理解,有了这行注释就可以直接在命令行运行groovy脚本文件啦。当然前提是电脑上要安装了Groovy,且要把Groovy加入path环境变量,注意这种注释中#必须是第一个字符,否则会报编译错误。

1
2
#!/usr/bin/env groovy
println "Hello from the shebang line"

Groovy关键字

Groovy语法的关键字如下所示:

as assert break finally implements new switch trait
case catch class false import null符号 switch true
const continue def for in package this try
default do else goto instanceof return throw while
enum enum extends if interface super throws
### 标识符
#### 正常的标识符
标识符可以由字母、美元符号以及下划线开头,不能以数字开头。
字符的范围如下:
* ‘a’ to ‘z’ (lowercase ascii letter)
* ‘A’ to ‘Z’ (uppercase ascii letter)
* ‘\u00C0’ to ‘\u00D6’
* ‘\u00D8’ to ‘\u00F6’
* ‘\u00F8’ to ‘\u00FF’
* ‘\u0100’ to ‘\uFFFE’

如下为有效的标识符:

1
2
3
4
def name
def item3
def with_underscore
def $dollarStart

如下的标识符就是非法的:

1
2
3
def 3tier
def a+b
def a#b

所有的关键字如果跟在一个dot后面也都是合法的标识符:

1
2
3
4
5
foo.as
foo.assert
foo.break
foo.case
foo.catch

引用标识符

引用标识符出现在一个打点运算符之后,如下所示:

1
2
3
4
5
6
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允许多种类型的字符串,后面会讲到。这些字符串都可以出现在打点运算符之后,如下所示:

1
2
3
4
5
6
map.'single quote'
map."double quote"
map.'''triple single quote'''
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$

有一种特殊的Groovy GStrings,也叫做插值字符串,如下所示:

1
2
3
def firstname = "Homer"
map."Simpson-${firstname}" = "Homer Simpson" //被插值为Simpson-Homer
assert map.'Simpson-Homer' == "Homer Simpson"

字符串

Groovy支持两种字符串,一种是java原生的java.lang.String,一种是groovy.lang.GString,叫做插值字符串。

单引号字符串

单引号字符串就是java.lang.String,不支持插值,如下所示:

1
'a single quoted string'

字符串连接

所有的Groovy字符串均支持+操作,如下所示:

1
assert 'ab' == 'a' + 'b'

三单引号字符串

三单引号字符串如下所示:

1
'''a triple single quoted string'''

三单引号支持多行,也是java.lang.String类型,不支持插值,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def aMultilineString = '''line one
line two
line three'''

def startingAndEndingWithANewline = '''
line one
line two
line three
'''

def strippedFirstNewline = '''\
line one
line two
line three
'''

assert !strippedFirstNewline.startsWith('\n')
转义特殊字符

可以使用反斜杠字符转义单引号字符,这样就可以避免字符串的终止:

1
'an escaped single quote: \' needs a backslash'

可以使用双重反斜杠转义反斜杠,如下所示:

1
'an escaped escape character: \\ needs a double backslash'

如下所示为转义字符对照表:
转义字符对照表

Unicode转义序列

对于键盘上没有出现的字符,可以使用一个反斜杠+’u’+四个十六进制数字表示。例如欧元符号可以使用一下方式表示:

1
'The Euro currency symbol: \u20AC'

双引用字符串

双引用字符串如下所示:

1
"a double quoted string"

对于双引用字符串来说,如果其中没有插值表达式那就是java.lang.String类型,否则就是groovy.lang.GString类型。

字符串插值

在Groovy所有的字符串字面量表示中,除了单引用和三引用字符串,其他的均支持插值。所谓字符串插值:就是将占位表达式的值替换到字符串中相应的位置当中,如下所示:

1
2
3
4
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}" //把name插入到greeting当中

assert greeting.toString() == 'Hello Guillaume'

还支持算数运算符:

1
2
def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5'

在${}当中还支持表达式,如下所示:

1
"The sum of 1 and 2 is equal to ${def a = 1; def b = 2; a + b}"

还支持$占位符,当使用点号表达式时:

1
2
def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'

如下是非法的,会抛出groovy.lang.MissingPropertyException异常,因为系统会认为你在获取一个number的toString属性,从而报错。

1
2
3
4
def number = 3.14
shouldFail(MissingPropertyException) {
println "$number.toString()"
}

如果你想去掉GString中的插值,只需要一个反斜杠即可:

1
assert '${name}' == "\${name}" //"\${name}"就和普通的'${name}'相等了,因为去掉了插值
插值闭包表达式

插值占位符还支持闭包表达式,如下所示:

1
2
3
4
5
def sParameterLessClosure = "1 + 2 == ${-> 3}" //这个闭包表达式没有参数
assert sParameterLessClosure == '1 + 2 == 3'

def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" //这个闭包表达式有一个java.io.StringWriter类型的参数
assert sOneParamClosure == '1 + 2 == 3'

闭包的一个最大的好处是惰性求值lazy evaluation,如下所示:

1
2
3
4
5
6
7
8
9
10
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" //eagerGString的值已经被固定了
assert lazyGString == "value == 2" //lazyGString的值被重新计算
和java进行交互

当一个方法需要java.lang.String参数,传入的却是一个GString类型的参数,这个参数的toString()方法就会被自动调用,看起来像我们可以直接将一个GString赋值给一个String变量一样:

1
2
3
4
5
6
7
8
9
10
11
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 String
assert result == 'The message is hello'
GString和String的hashCode

GString和String的hashCode是不一样的,即便他们的最终结果是一样:

1
assert "one: ${1}".hashCode() != "one: 1".hashCode()

因此在Map当中不能不能使用GString作为Key值,如下所示:

1
2
3
4
defkey= "a"
def m = ["${key}": "letter ${key}"] // key类型是一个GString

assert m["a"] == null // 用一个普通String类型的key去取值,会找不到这个key,因此就会取不到值

三双引号字符串

三双引号字符串类似于双引号字符串,但是是多行的,因此又类似于三引号字符串:

1
2
3
4
5
6
7
8
9
10
11
12
def name = 'Groovy'
def template = """
Dear Mr ${name},

You're the winner of the lottery!

Yours sincerly,

Dave
"""

assert template.toString().contains('Groovy')

斜线字符串

除了使用引号来括住字符串,还可以使用/,斜线字符串一般用来定义正则表达式:

1
2
def fooPattern = /.*foo.*/
assert fooPattern == '.*foo.*'

只有正斜线需要用反斜线转义:

1
2
def escapeSlash = /The character \/ is a forward slash/
assert escapeSlash == 'The character / is a forward slash'

斜线字符串是多行的:

1
2
def escapeSlash = /The character \/ is a forward slash/
assert escapeSlash == 'The character / is a forward slash'

斜线字符串也可以被插值:

1
2
3
4
defcolor= 'blue'
def interpolatedSlashy = /a ${color} car/

assert interpolatedSlashy == 'a blue car'

注意:一个空的斜线字符串不能使用两个正斜线表示,因为Groovy会把其理解为注释。因此,下面的断言不会被编译,因为这个是一个非终止的语句:

1
assert '' == //

美元斜线字符串

这种字符串使用$/开始,使用/$结束,其中的转义字符为$:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 opening dollar slashy
$/$$ escaped closing dollar slashy
/$

assert [
'Guillaume',
'April, 1st',
'$ dollar sign',
'$ escaped dollar sign',
'\\ backslash',
'/ forward slash',
'/ escaped forward slash',
'$/ escaped opening dollar slashy',
'/$ escaped closing dollar slashy'
].every { dollarSlashy.contains(it) }

字符串总结

字符串总结

字符

Groovy当中并没有明确的字符字面量,需要明确指明:

1
2
3
4
5
6
7
8
char c1 = 'A'
assert c1 instanceof Character

def c2 = 'B' as char
assert c2 instanceof Character

def c3 = (char)'C'
assert c3 instanceof Character

数字

Groovy支持不同类型的整型字面量和小数字面量。

整型字面量

支持的整型字面量和java是一样的:

  • byte
  • char
  • short
  • int
  • long
  • java.lang.BigInteger

如下所示:

1
2
3
4
5
6
7
8
9
// primitive types
byte b = 1
char c = 2
short s = 3
int i = 4
long l = 5

// infinite precision
BigInteger bi = 6

当使用def指明整数字面量时,变量的类型会根据数字的大小自动调整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

对于负数也是如此:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
数字的非十进制表示

数字可以用二进制、八进制、16进制以及小数表示。
数字二进制表示如下,以ob开头:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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开头:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

数字的16进制表示如下,以0x开头:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

小数字面量

小数字面量也跟java是一样的:

  • float
  • double
  • java.lang.BigDemical

如下所示:

1
2
3
4
5
6
// primitive types
float f = 1.234
double d = 2.345

// infinite precision
BigDecimal bd = 3.456

小数还支持科学计数法:

1
2
3
4
5
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

为了精确的计算小数,groovy选择java.lang.BigDecimal作为其小数类型。此外,float和double也是支持的小数类型,但是这俩类型需要一个显式类型声明、强制类型转换或后缀声明。

1
2
def decimal = 123.456
println decimal.getClass() // class java.lang.BigDecimal

字面中的下划线

1
2
3
4
5
6
7
8
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

如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
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类型,否则就是BigDemical类型。

指数运算

运算表如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 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

布尔型

如下所示:

1
2
3
def myBooleanVariable = true
boolean untypedBooleanVar = false
boolean Field = true

true和false只是两个基础的布尔值,关于更复杂的boolean操作,参考:
logical operators.
special rules

列表List

Groovy列表就是java.util.List,默认的子类就是java.util.ArrayList,如下所示:

1
2
3
4
def numbers = [1, 2, 3]         

assert numbers instanceof List
assert numbers.size() == 3

列表中可以支持各种类型:

1
def heterogeneous = [1, "a", true]

还可以定义各种类型的List,默认是ArrayList:

1
2
3
4
5
6
7
8
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追加元素,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def letters = ['a', 'b', 'c', 'd']

assert letters[0] == 'a'
assert letters[1] == 'b'

assert letters[-1] == 'd' //获取最后一个元素,-1是从数组末尾开始的第一个元素
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'] // 一次性获取两个元素,返回一个新的List
assert letters[2..4] == ['C', 'd', 'e'] //使用一个范围获取范围内的元素,返回一个新的List

还可以组成多维List:

1
2
def multi = [[0, 1], [2, 3]]     
assert multi[1][0] == 2

数组

数组需要显式定义数组的类型:

1
2
3
4
5
6
7
8
9
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

可以定义多维数组:

1
2
3
4
5
6
def matrix3 = new Integer[3][3]         
assert matrix3.size() == 3

Integer[][] matrix2
matrix2 = [[1, 2], [3, 4]]
assert matrix2 instanceof Integer[][]

获取数组元素的方式跟List一样:

1
2
3
4
5
String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']
assert names[0] == 'Cédric'

names[2] = 'Blackdrag'
assert names[2] == 'Blackdrag'

映射表Maps

如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
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 //默认是LinkedHashMap类型

当获取一个map中不存在的key,会返回null:

1
assert colors.unknown == null

除了使用string类型的key,还可以使用其他类型的key:

1
2
3
def numbers = [1: 'one', 2: 'two']

assert numbers[1] == 'one'

如果key是一个变量,如下所示:

1
2
3
4
5
def key = 'name'
def person = [key: 'Guillaume'] //'Guilaume'对应的key为"key",而不是变量key所关联的值

assert !person.containsKey('name') //不包含'name'这个key
assert person.containsKey('key') //包含'key'这个key

要想解决上述问题,可以如下所示:

1
2
3
4
5
def key = 'name'
person = [(key): 'Guillaume'] //此时'Guilaume'对应的key就是变量key所对应的值

assert person.containsKey('name')
assert !person.containsKey('key')

以上就是Groovy的基本语法,关于Groovy的语法特性,还包含一下几个方面,直接看官方文档即可,有兴趣的可以了解下。
运算符 Operators
程序结构 Program structure
Groovy 面向对象语法 Object orientation
闭包 Closures
Groovy 语义 Semantics