扫盲系列 - Groovy 语法
构建工具是一个可编程的工具,能够以执行和有序的任务来表达满足需要的自动化过程
构建工具就是用来让我们不在做机械重复的事情,解放我们的双手
DSL
领域特定语言,基本思想是“求专不求全”,不像通用目的语言那样目标覆盖一切软件问题,而是专门针对某一特定问题的计算机语言,通俗的讲就是行话
Gradle
是一个构建工具,基于 groovy ,并且是一种 DSL , Gradle 样板文件的代码少,这是因为它的 DSL 被设计用于解决特定的问题:贯穿软件的生命周期,从编译,到静态检查,直到打包和部署。
Groovy
是一个编程语言,
- 一个功能强大,灵活动态语言,他是在 Java 平台上的一种简洁,数学并且易学的语言,大大提高了开发者的效率
- 属于脚本语言
- 提供更简单,更灵活的语法,可以在运行时动态进行类型检查
- 执行 groovy 脚本的时候, groovy 会讲起编译成 Java 字节码,然后通过 JVM 来执行该 Java 类,
所以,在执行的时候,由于 Groovy 代码已经被编译为 Java 代码, JVM 根本就不知道自己运行的是 Groovy 代码,
Groovy简单语法
字符串
def d="world"
def a="hello $d " //双引号的内容,如果符号中有$的话,会先求$表达式的值,在输出
println a //hello world
def b='hello $d' //如果是单引号的话,不对$符号转义
println b //hello $d
容器类
- List 链表 对应 Java 中 的ArrayList
- Map键值表 对应 Java 中的LinkedHashMap
- Range范围 对 List 的扩展
List 链表
格式 []
def list=[4,5]
def list2 = new ArrayList<String>(list1) //创建新集合
def list3 = ['a', 1 , 'a', 'a', 2.5, 2.5f, 2.5d, 'hello', 7g , null , 9 as byte] `//可以不同类型,并且可以重复,可以为null`
def list=[1,'2', true ,"helei",null]
println list.size() //打印长度
//list.get(-1) //数组角标越界
println list[-2] //倒数第二个 helei
println list[-1] //倒数第一个 null
println list[0] //整数第一个 1
println list[3] //整数第四个 helei
println list.getAt(-2) //倒数第二个 helei
println list.getAt(2) //整数第二个 true
//集合遍历
list.each{ //不带下标
print "item $it "
}
//相当于下一种, each 方法接受一个闭包参数,
list.each({
it->print "$it "
})
list.eachWithIndex{ //带下标的
it ,i->
println "$i,$it "
}
def list=[5,"hello",'world', null ,true]
println list[8] //null
println list.size() //5
list[10]=100 //设置第 11 个元素为100
println list.size() //集合大小为 11
//没有赋值的元素为null
println list // [5, hello , world , null , true , null , null , null , null , null , 100]
Map 键值表
使用[:]创建 Map 注意其中的冒号。冒号左边是 key ,右边是 Value 。key必须是字符串, value 可以是任何对象
。另外, key 可以用’‘或”“包起来,也可以不用引号包起来。
def map=[name:"zhangsan","sex":true,id:123]
println map.size()
println map.get("name")
println map.name
println map.age //要是想获取没有属性的值,返回的是null
//添加值
map.addone="age" //添加一个属性为 addone ,值为”age“
map.put("aa",false) //添加一个属性为 aa ,值为flase
//集合遍历
map.each{
entry->//entry 是 map 的一个属性,
println "key:$entry.key value: $entry.value"
}
map.eachWithIndex{
entry ,i->
println "$i key:$entry.key value: $entry.value"
}
map.each{
key ,value->
println "key: $key value: $value"
}
map.eachWithIndex{
key , value ,i->
println "$i key:$key value: $value"
}
Range 类
是 List 的一个变体,能够很方便的创建有顺序的集合
- 使用 .. 创建左右都包含的Range
- 使用.. <创建左包含,右边不包含的Range
def range=5..8 //[5, 6 , 7 , 8]
println range
range=5..<8 //[5, 6 , 7] 不包含8
println range
println range.from+" , "+range.to //打印集合的起始值和末尾值 5 7
for(i in range){
println "hello $i"
}
(1..10).each{//each 方法遍历,和 List 一样
i->
print "hello $i "
}
def getRate(year){
//特殊用法,用在 switch 语句中
switch (year){
case 1..10://如果输入的年份在 1 到 10 之间,执行
interestRate=0.076
break
case 11..25://如果年份在 11 到 25 之间,执行
interestRate=0.052
break
default :interestRate=0.037
}
}
println getRate(2) //0.076
println getRate(22) //0.052
println getRate(32) //0.037
Groovy 中的闭包
闭包是一段开放的,匿名的代码,用大括号{}括起来的代码块,这个代码块在调用的时候触发,
闭包格式
{
[closureParameters ->]
statements
}
闭包[closureParameters ->] 是可选的,看起来像是一个 List ,并且这些参数的类型可以不写
例如
{ String x, int y -> //参数
println "hey ${x} the value is ${y}" //statement语句
}
创建闭包
闭包是一种类型,所以定义的时候要用=给它赋值,这是跟方法的本质区别 三种方法
// 使用 def 声明一个闭包
def listener={e -> println "Clicked on $e.source"}
// 使用指定类型 Closure 声明一个闭包
Closure callback={ println 'Done !'} //这个同时省略了参数
//使用泛型,生命一个固定返回类型的闭包
Closure<Boolean > isTextFile={
File it ->it.name.endsWith('.txt')//返回 boolean 类型
}
调用闭包
闭包是一个匿名代码块,不能像调用方法那样调用,
- 无参闭包
def code={123}
println code()//调用该闭包
println code.call()//另外一种闭包方式
- 带参数的闭包
Closure isOldNumber={ int i-> //声明参数, int 可以省略 i%2==1 //可以省略 return 语句 } println isOldNumber(32) println isOldNumber.call(4)
闭包参数说明
- 普通的参数
- 可以不写参数类型
- 可以给参数一个默认值
Closure closureWithTwoArgs =//使用指定类型 Closure 定义闭包,
{
a ,b ->// 无参数类型,
a+b //传入不能相加的类型会报错
}
println closureWithTwoArgs(1,2) //3
println closureWithTwoArgs(1,2.5)// 3.5
println closureWithTwoArgs(1,'2')// 12 因为'2'转换成相应的 AICCS 码表对应的值了
println closureWithTwoArgs(1,"q")// 1q 这种是把数字 1 当成了字符串拼接了
//默认参数
def closureWithTwoArgsAndDefalutValue={
int a ,int b=2 ->
a+b
}
println closureWithTwoArgsAndDefalutValue(1) //3
println closureWithTwoArgsAndDefalutValue(1,3)// 3.5
//println closureWithTwoArgsAndDefalutValue(1,'2')// 报错,类型已经指定
- 隐式参数
如果闭包没有定义参数的话,则隐含一个参数,这个参数名字叫 it ,它保存了返回这个闭包的一个值,
def greeting={"hello ,$it !"}//隐含参数 it ,这个 it 是任意参数类型
//相当于
Closure greetingT={
it ->
//return "hello $it !" //GString 用法
return "hello,"+it+"!"
}
println greeting("world")//hello , world !
println greeting(1)//hello , 1 !
println greetingT("world")//hello, world !
def isOddNumber={it %2==1}// 这种情况下 it 类型就是int
println isOddNumber(3)
// println isOddNumber(3.5) //报错
// println isOddNumber('3')// 报错
def noParamClousre={-> true}
//绝对不能传递参数的
println noParamClousre()
// println noParamClousre("111") 报错
- 可变参数
def aMethod ={
int... args -> // ... 表示可变
int sum=0;
args.eachWithIndex{// 遍历求和
it ,i->
sum=sum+args[i]
}
return sum
}
println aMethod(1, 2 ,3)
闭包的 Delegation 代理
Groovy 中的闭包具有能够改变代理或代理策略的能力,使得我们能够使用它设计很优美的 DSL 语言
- 理解 this ,owner
Groovy 中的闭包内置了 3 个不同的对象,可以直接使用
- this 闭包躲在的最近的类 .class
- owner 定义闭包的宿主,不仅仅是类,还可能是一个闭包
- delegate 代理
class NestedClocure {
void run(){
def nestedClosure={ //嵌套闭包
def cl={ owner} //owner 值的就是该闭包的宿主,即nestedClosure
println cl() //NestedClocure$_run_closure1@33f6a532
def cl2={this}// this 指的是最近的 class ,即NestedClocure.class
println cl2() //NestedClocure@d9081e1
}
println nestedClosure()
println nestedClosure //NestedClocure$_run_closure1@4658ef71
//println nestedClosure()==nestedClosure
}
}
def n=new NestedClocure()
n.run()
- 理解delegate this 和 owner 是 Groovy 内置在 Closure 中 的对象,那么 delegate 就是用户指定的对象,用于 Closure 使用。 delegate 默认使用的是owner
class Enclosing {
void run(){
def cl={getDelegate()}// 使用 getDelegate() 方法获取 Closure 的delegate
def cl2={delegate}// 使用delegate
println cl()==cl2() //true
println cl()==this //true
def enclosed={
{-> delegate}.call()
}
println enclosed()==enclosed //true
}
}
def e=new Enclosing()
e.run()
delegate 可以设置为不同的对象
class Person {
String name
}
class Thing {
String name
}
def p=new Person(name:'Norman')
def t=new Thing(name : 'Teapot')
//定义一个闭包,使用 delegate 获取名字,并返回大写值
def upperCassName={delegate.name.toUpperCase()}
upperCassName.delegate=p //设置为 p ,则返回NORMAN
println upperCassName()=='NORMAN'
upperCassName.delegate=t //设置为 p ,则返回TEAPOT
println upperCassName()=='TEAPOT'
// delegate 对象可以透明使用,即不需要在方法前面显示声明delegate
def p1=new Person(name : 'helei')
//并没有写delegate.name,默认使用 owner , owner 没有 name 属性,所以使用 delegate 的 name 属性
def cl={name.toUpperCase()}
cl.delegate=p1
println cl()=="HELEI"
- delegate strategy 代理的代理策略 delegate 的策略默认是Closure.OWNER_FRIST,即 owner 优先,所有在宿主没有属性,就会去 delegate 中查查,如果 delegate 也没有,就报错了,
delegate 的策略
- Closure.OWNER_FIRST
- Closure.DELEGATE_FIRST
- Closure.OWNER_ONLY
- Closure.DELEGATE_ONLY
- Closure.TO_SELF
class Person{
String name
int age
def fetchAge={age}
}
class Thing{
String name
}
def p=new Person(name:'helei',age:42)
def t=new Thing(name: 'Printer')
def cl=p.fetchAge
cl.delegate=p
println cl()
cl.delegate=t
println cl()// 42 owner 优先, fetachAge 是一个闭包,他的 owner 是Person
cl.resolveStrategy=Closure.DELEGATE_ONLY //修改策略
cl.delegate=p
println cl() //42
cl.delegate=t
//修改策略后,仅在 delegate 查找,因为 Thing 中没有有 Age 属性,所以报错
// println cl() //Thing 中没有 Age 属性,所以报错
例子解析
一
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
}
- dependencies 是一个函数名,接受的参数是一个闭包类型,这是一个方法调用,而不是函数定义,因为在
groovy中方法调用是可以省略()
dependencies ({ classpath 'com.android.tools.build:gradle:2.1.2' })
注意和上一个比较,在大括号外部添加了一对小括号
- {} 实际上是一个 groovy 的闭包类型的参数,并且这个闭包是无参数的,相当于
{it -> classpath 'com.android.tools.build:gradle:2.1.2' }
- 闭包里面由执行了 classpath 这个方法,而 classpath 方法的调用同样省略了(),实际上是
classpath ('com.android.tools.build:gradle:2.1.2')
所以,整个代码补全后相当于
dependencies ({ it -> classpath('com.android.tools.build:gradle:2.1.2') })
二
task clean(type: Delete) {
delete rootProject.buildDir
}
- task 是一个方法,参数因为中间没有逗号,所以只有一个,
task ( clean(type: Delete) { delete rootProject.buildDir })
- clean是一个方法。里面有两个参数,一个是 Map ,一个是闭包
即
def clean(Map type,Closure cl){ type.type }
把执行的 clean 返回的结果传递给方法task
- clean(type: Delete) 是往 clean 的 Map 集合中存放一个键值对,键是 type ,值是 Delete ,
- 因为根据 Groovy 的语法特性,
当一个方法的最后一个参数是闭包,可以将这个闭包写在()外面。
clean(type: Delete) { delete rootProject.buildDir }
所以相当于
clean(type: Delete, { delete rootProject.buildDir })
- 所以,整个改编后,
task ( clean(type: Delete, { delete rootProject.buildDir }) ) //一个一个单独写出来,就是如下所示 def closure={delete rootProject.buildDir} def result=clean(type:Delete,closure()) task result
搬运地址:
既已览卷至此,何不品评一二: