在使用Android Studio开发app的过程中,都会接触到Gradle配置,而Gradle的配置是基于Grovvy语法的。因此,要想熟练的使用Gradle进行配置,就必须熟悉Groovy语法,下面开始讲解下Groovy的基本语法。
Groovy可以看作是java的加强版,扩展了java的语法,拥有自己的一些特性。
Groovy语法官方文档
单行注释如下所示,跟java一样:
// a standalone single line comment println "hello" // a comment till the end of the line
多行注释如下所示,跟java一样:
/* 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跟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}" } }
除了单行注释,还有一种特殊的单行注释,如下所示,叫做Shebang line(各位可以自行去翻译)。这行注释主要是便于unix理解,有了这行注释就可以直接在命令行运行groovy脚本文件啦。当然前提是电脑上要安装了Groovy,且要把Groovy加入path环境变量,注意这种注释中#必须是第一个字符,否则会报编译错误。
#!/usr/bin/env groovy println "Hello from the shebang line"
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 |
标识符可以由字母、美元符号以及下划线开头,不能以数字开头。
字符的范围如下:
如下为有效的标识符:
def name def item3 def with_underscore def $dollarStart
如下的标识符就是非法的:
def 3tier def a+b def a#b
所有的关键字如果跟在一个dot后面也都是合法的标识符:
foo.as foo.assert foo.break foo.case foo.catch
引用标识符出现在一个打点运算符之后,如下所示:
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,也叫做插值字符串,如下所示:
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,不支持插值,如下所示:
'a single quoted string'
所有的Groovy字符串均支持+操作,如下所示:
assert 'ab' == 'a' + 'b'
三单引号字符串如下所示:
'''a triple single quoted string'''
三单引号支持多行,也是java.lang.String类型,不支持插值,如下所示:
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')
可以使用反斜杠字符转义单引号字符,这样就可以避免字符串的终止:
'an escaped single quote: \' needs a backslash'
可以使用双重反斜杠转义反斜杠,如下所示:
'an escaped escape character: \\ needs a double backslash'
如下所示为转义字符对照表:
对于键盘上没有出现的字符,可以使用一个反斜杠+’u’+四个十六进制数字表示。例如欧元符号可以使用一下方式表示:
'The Euro currency symbol: \u20AC'
双引用字符串如下所示:
"a double quoted string"
对于双引用字符串来说,如果其中没有插值表达式那就是java.lang.String类型,否则就是groovy.lang.GString类型。
在Groovy所有的字符串字面量表示中,除了单引用和三引用字符串,其他的均支持插值。所谓字符串插值:就是将占位表达式的值替换到字符串中相应的位置当中,如下所示:
def name = 'Guillaume' // a plain string def greeting = "Hello ${name}" //把name插入到greeting当中 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'
在${}当中还支持表达式,如下所示:
"The sum of 1 and 2 is equal to ${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'
如下是非法的,会抛出groovy.lang.MissingPropertyException异常,因为系统会认为你在获取一个number的toString属性,从而报错。
def number = 3.14 shouldFail(MissingPropertyException) { println "$number.toString()" }
如果你想去掉GString中的插值,只需要一个反斜杠即可:
assert '${name}' == "\${name}" //"\${name}"就和普通的'${name}'相等了,因为去掉了插值
插值占位符还支持闭包表达式,如下所示:
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,如下所示:
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.lang.String参数,传入的却是一个GString类型的参数,这个参数的toString()方法就会被自动调用,看起来像我们可以直接将一个GString赋值给一个String变量一样:
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是不一样的,即便他们的最终结果是一样:
assert "one: ${1}".hashCode() != "one: 1".hashCode()
因此在Map当中不能不能使用GString作为Key值,如下所示:
defkey= "a" def m = ["${key}": "letter ${key}"] // key类型是一个GString assert m["a"] == null // 用一个普通String类型的key去取值,会找不到这个key,因此就会取不到值
三双引号字符串类似于双引号字符串,但是是多行的,因此又类似于三引号字符串:
def name = 'Groovy' def template = """ Dear Mr ${name}, You're the winner of the lottery! Yours sincerly, Dave """ assert template.toString().contains('Groovy')
除了使用引号来括住字符串,还可以使用/,斜线字符串一般用来定义正则表达式:
def fooPattern = /.*foo.*/ assert fooPattern == '.*foo.*'
只有正斜线需要用反斜线转义:
def escapeSlash = /The character \/ is a forward slash/ assert escapeSlash == 'The character / is a forward slash'
斜线字符串是多行的:
def escapeSlash = /The character \/ is a forward slash/ assert escapeSlash == 'The character / is a forward slash'
斜线字符串也可以被插值:
defcolor= 'blue' def interpolatedSlashy = /a ${color} car/ assert interpolatedSlashy == 'a blue car'
注意:一个空的斜线字符串不能使用两个正斜线表示,因为Groovy会把其理解为注释。因此,下面的断言不会被编译,因为这个是一个非终止的语句:
assert '' == //
这种字符串使用$/开始,使用/$结束,其中的转义字符为$:
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当中并没有明确的字符字面量,需要明确指明:
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是一样的:
如下所示:
// primitive types 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
数字可以用二进制、八进制、16进制以及小数表示。
数字二进制表示如下,以ob开头:
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
数字的16进制表示如下,以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
小数字面量也跟java是一样的:
如下所示:
// 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
为了精确的计算小数,groovy选择java.lang.BigDecimal作为其小数类型。此外,float和double也是支持的小数类型,但是这俩类型需要一个显式类型声明、强制类型转换或后缀声明。
def decimal = 123.456 println decimal.getClass() // class java.lang.BigDecimal
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
以下是数学运算表(除法运算和指数运算例外):
如果两个数中其中有一个是float或double类型,那么除法运算/或者/=得到的结果就是double类型,否则就是BigDemical类型。
运算表如下所示:
// 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
如下所示:
def myBooleanVariable = true boolean untypedBooleanVar = false boolean Field = true
true和false只是两个基础的布尔值,关于更复杂的boolean操作,参考:
logical operators.
special rules
Groovy列表就是java.util.List,默认的子类就是java.util.ArrayList,如下所示:
def numbers = [1, 2, 3] assert numbers instanceof List assert numbers.size() == 3
列表中可以支持各种类型:
def heterogeneous = [1, "a", true]
还可以定义各种类型的List,默认是ArrayList:
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追加元素,如下所示:
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:
def multi = [[0, 1], [2, 3]] assert multi[1][0] == 2
数组需要显式定义数组的类型:
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() == 3 Integer[][] matrix2 matrix2 = [[1, 2], [3, 4]] assert matrix2 instanceof Integer[][]
获取数组元素的方式跟List一样:
String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul'] assert names[0] == 'Cédric' names[2] = 'Blackdrag' assert names[2] == 'Blackdrag'
如下所示:
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:
assert colors.unknown == null
除了使用string类型的key,还可以使用其他类型的key:
def numbers = [1: 'one', 2: 'two'] assert numbers[1] == 'one'
如果key是一个变量,如下所示:
def key = 'name' def person = [key: 'Guillaume'] //'Guilaume'对应的key为"key",而不是变量key所关联的值 assert !person.containsKey('name') //不包含'name'这个key assert person.containsKey('key') //包含'key'这个key
要想解决上述问题,可以如下所示:
def key = 'name' person = [(key): 'Guillaume'] //此时'Guilaume'对应的key就是变量key所对应的值 assert person.containsKey('name') assert !person.containsKey('key')