前言
在 Android
中现在默认的构建工具是 Gradle
,而 Gradle
构建工具是使用 Groovy
语言编写的,这就使得了解 Groovy
语言变得非常重要了。
如果你不了解 Groovy
会以为 build.gralde
文件中写的就只是一些配置,其实并没有这么简单。之所以看起来项配置文件,是因为 Groovy
的闭包特性,让我们写起来简单,看起来像配置文件,这就降低了编写的门槛。使得我们可以专注于 Android
层面的业务逻辑。
随着 Android
的发展,我们不能局限于应用层,在构建的过程我们也可以进行一些优化。今天就来学习一下 Groovy
这们语言。
安装
安装很简单,我们可以从Groovy官网下载安装包,也可以通过包管理器来安装, macOS
下可以通过如下命令安装
1
2
3
|
brew install groovy
# 下面这条指令也可以,根据自己的系统选择一个常用的就行
curl -s get.sdkman.io | bash
|
安装好之后我们可以通过如下指令验证是否安装成功
1
2
|
groovy --version
Groovy Version: 3.0.3 JVM: 1.8.0_45 Vendor: Oracle Corporation OS: Mac OS X
|
可以看到输出了 groovy
的版本。
基础语法
变量
在 groovy
中声明变量有两种,一种是自动推导。编译器会根据你写的字面量去推导出合适的类型,自动推导的都是对象类型,不是基本数据类型。
如果你要使用基本数据类型,就只能直接指定变量的类型。如下所示
1
2
3
4
5
6
7
8
|
// 自动推导
def name = "groovy"
// 指定变量类型
int age = 3
println name.class
println age.class
|
输出结果如下
1
2
|
class java.lang.String
class java.lang.Integer
|
可以看到即便我们指定了变量的类型,最后还是会被装换成对象类型。
这里还要一个地方要注意的就是,浮点数会被推导为 BigDecimal
而不是 Double
1
2
3
|
def pi = 3.14
println pi.class
|
输出如下所示
1
|
class java.math.BigDecimal
|
字符串
groovy
中的字符串与 Java
中的不同,更像是 Python
中的字符串,支持单引号,双引号,三引号的写法。
1
2
3
4
5
6
7
8
9
10
11
|
def str1 = 'str1'
def str2 = "str2"
def str3 = '''str3 line1
str3 line2
str3 line3'''
def str4 = """
str4 line2
str4 line3
str4 line4
"""
|
输出如下所示
1
2
3
4
5
6
7
8
9
|
str1
str2
str3 line1
str3 line2
str3 line3
str4 line2
str4 line3
str4 line4
|
其中双引号可以支持在字符串中引用其他变量,这时候的类型为 GStringImpl
1
2
3
4
5
6
7
|
def name = "groovy"
def age = 3
def info = "the ${name} age is: ${age}"
println info
println info.class
|
输出如下所示
1
2
|
the groovy age is: 3
class org.codehaus.groovy.runtime.GStringImpl
|
可以看到通过 $
可以引用别的变量,这时候字符串的类型为 org.codehaus.groovy.runtime.GStringImpl
而不再是 String
了。它们之间可以相互赋值使用,不需要强制类型转换。
使用这种方式拼接字符串就很方便,非常直观,不用写一堆的 +
。这中方式也叫模板字符串。在 ${}
中还可以进行求值计算。
闭包
简单使用
闭包和 Java
中的 lambda
表达式类似
1
2
3
4
5
6
7
|
def closure = {
println "Hello Groovy Closure"
}
closure()
closure.call()
|
输出如下所示
1
2
|
Hello Groovy Closure
Hello Groovy Closure
|
可以看到闭包就像一个函数一样,可以通过函数调用也可以通过 call()
来调用。
带有参数的闭包
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
|
// 1 带有参数的闭包
def add = { x, y ->
x + y
}
println add(1,2)
// 参数有类型
def add1 = { int x, int y ->
x + y
}
println add(1,2)
// 参数有返回值
def add2 = { int x, int y ->
return x + y
}
println add2(1,2)
def increment = {
it + 1
}
println increment(1)
|
输出结果如下
1
2
3
4
|
the add(1,2) result is: 3
the add1(1,2) result is: 3
the add2(1,2) result is: 3
the increment(1) result is: 2
|
闭包的参数类型可以不指定,闭包的最后一行代码的执行结果默认会作为返回值。
如果闭包只有一个参数,我们可以不指定参数名,闭包会提供一个 it
来代替。
闭包委托策略
闭包在 groovy
中也是一个对象,其类名是 groovy.lang.Closure
。闭包的委托策略是其独有的,再了解委托策略之前我们先来了解一下闭包中的 this
、 owner
、 delegate
- this: 表示闭包在哪个类定义的
- owner: 表示闭包直接定义的地方,可以是个闭包或者类,这种情况通常是在闭包嵌套的时候会和
this
不一样
- delegate: 用来确定方法的调用者或者属性是哪个对象的,默认和
owner
一致。代表的是第三方对象,在这个第三方对象中你可以找到闭包中未定义的方法调用或者属性。
先来看个例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
def closure = {
println "this is:" + this
println "owner is:" + owner
println "delegate is:" + delegate
def innerClosure = {
println "innerClosure this is:" + this
println "innerClosure owner is:" + owner
println "innerClosure delegate is:" + delegate
}
innerClosure()
}
closure()
|
输出结果如下
1
2
3
4
5
6
|
this is:Main@7fcbe147
owner is:Main@7fcbe147
delegate is:Main@7fcbe147
innerClosure this is:Main@7fcbe147
innerClosure owner is:Main$_run_closure1@36b0fcd5
innerClosure delegate is:Main$_run_closure1@36b0fcd5
|
可以看到默认它们都指向同一个对象。当闭包嵌套的时候 this
与 owner
、 delegate
不同。
既然在嵌套的时候 this
与 owner
不同,那什么时候 owner
与 delegate
不同?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Person {
}
def closure = {
println "this is:" + this
println "owner is:" + owner
println "delegate is:" + delegate
}
def p = new Person()
closure.delegate = p
closure()
|
输出如下所示
1
2
3
|
this is:Main@410ae9a3
owner is:Main@410ae9a3
delegate is:Person@25ddbbbb
|
delegate
指向了 Person
对象并且 delegate
是可以被修改的,而 this
和 owner
则不可以被修改。那 delegate
有什么用呢?来看下面的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Person {
String name
def pretty = { "My name is $name" }
String toString() {
pretty()
}
}
class Thing {
String name
}
def p = new Person(name: 'Groovy')
def t = new Thing(name: 'Java')
println p.toString()
p.pretty.delegate = t
println p.toString()
|
输出结果如下
1
2
|
My name is Groovy
My name is Groovy
|
可以看到我们改了 pretty
闭包的 delegate
但是并没有任何效果。这是因为 delegate
的默认策略是 Closure.OWNER_FIRST
,所以没有生效。
我们可以通过更改 delegate
的 resolveStrategy
为 Closure.DELEGATE_FIRST
来使其生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Person {
String name
def pretty = { "My name is $name" }
String toString() {
pretty()
}
}
class Thing {
String name
}
def p = new Person(name: 'Groovy')
def t = new Thing(name: 'Java')
println p.toString()
p.pretty.delegate = t
// 修改 delegate 策略
p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
println p.toString()
|
输出结果如下
1
2
|
My name is Groovy
My name is Java
|
这次更改就生效了。
resolveStrategy
可以有如下几种策略
Closure.OWNER_FIRST
默认为这个策略,先从 owner
中找属性或方法,找不到再从 delegete
中寻找。
Closure.DELEGATE_FIRST
先从 delegate
中查找,与 OWNER_FIRST
相反。
Closure.OWNER_ONLY
只在 owner
中寻找,找不到就报错
Closure.DELEGATE_ONLY
只在 delegate
中寻找,找不到就报错
Closure.TO_SELF
在闭包自身中寻找。
集合
在 groovy
中对 Java
的集合做了不少的加强,使得使用起来更加方便。
List
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def list = [1, 2, 3, 4, 5, 6]
def list1 = [1, "a", true]
println "the list is: ${list}"
println "ths list1 is: ${list1}"
// 默认是 ArrayList
println "the list class is: ${list.class}"
// 往 list 中添加元素
list << 7
println "the list after add is: ${list}"
// 获取 list 中某个区间
println "the list[0,2] is: ${list[0..2]}"
// 获取 list 中指定位置的值
println "the list[0,2] is: ${list[0,2]}"
|
输出结果如下
1
2
3
4
5
6
|
the list is: [1, 2, 3, 4, 5, 6]
ths list1 is: [1, a, true]
the list class is: class java.util.ArrayList
the list after add is: [1, 2, 3, 4, 5, 6, 7]
the list[0,2] is: [1, 2, 3]
the list[0,2] is: [1, 3]
|
其中 <<
是添加元素的语法糖,重载了左移操作符。其他和 ArrayList
的操作类似。
还要一个要注意的是,在 List
中可以添加任意类型的元素。
Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def map = [a: 1, b: 2, c: 3]
println "the a in map is: ${map['a']}"
println "the a in map is: ${map.a}"
// 添加元素
map['d'] = 4
println "after add map is: ${map}"
// 遍历
map.each { key, value ->
println "${key}:${value}"
}
map.eachWithIndex { key, value, index ->
println "the $index is $key : $value"
}
|
输出结果如下
1
2
3
4
5
6
7
8
9
10
11
|
the a in map is: 1
the a in map is: 1
after add map is: [a:1, b:2, c:3, d:4]
a:1
b:2
c:3
d:4
the 0 is a : 1
the 1 is b : 2
the 2 is c : 3
the 3 is d : 4
|
Map
中主要的变化就是使用 :
来区分键值对。
文件操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
def file = new File('./test.groovy')
// 写文件
file.createNewFile()
file.withWriter { writer ->
writer.append("println 'Hello Groovy'")
}
// 读文件
file.eachLine { line ->
println line
}
println file.getText()
// 获取每一行,返回是个数组
println file.readLines()
println file.withReader { reader ->
char[] buffer = new char[100]
reader.read(buffer)
return buffer
}
|
输出结果如下
1
2
3
4
|
println 'Hello Groovy'
println 'Hello Groovy'
[println 'Hello Groovy']
println 'Hello Groovy'
|
在 groovy
中操作文件比在 Java
中方便许多。
参考