Gradle学习1——Groovy基本介绍

学习Gradle,前前后后总结了一些内容,然后整理出了一个系列,共计10篇文章,与大家分享:

  1. Groovy基本介绍
  2. 开始使用Gradle
  3. 自定义属性
  4. 深入了解Task和构建生命周期
  5. Gradle增量式构建
  6. 挂接到构建生命周期
  7. 编写和使用自定义Task
  8. 依赖管理
  9. 多项目构建
  10. 自定义Gradle插件

学习本系列前可以下载相关的github项目gradleLearnDemo
地址:https://github.com/sososeen09/gradleLearnDemo

0 前言

Gradle脚本使用的是Groovy语言,Groovy也是基于JVM的一种动态语言,基于Java并扩展了Java语言。Groovy会使写Java程序就要写脚本一样简单,写完就可以执行,所以看起来也像是一门脚本语言。Groovy内部会把代码编译为Java class文件然后启动虚拟机来执行,这些过程,我们开发人员是不用管的。

Gradle是一种DSL,也即是 Domain Specific Language 领域特定语言。说白了DSL就是一种行话,这个语言在Gradle中可以识别,在其它地方就用不了了。

学习Gradle,我们不必要精通Groovy语言,但还是需要了解一些Groovy的基本知识,比如def关键字、返回语句、字符串、集合、闭包等。下面,我们就来简单介绍一下。

1 Groovy代码的执行

前面我们说了,Groovy很像是一门脚本语言,我们来看一下怎么回事。
创建一个HelloGroovy.groovy文件,里面就一行代码

println 'hello groovy!'

然后执行命令groovy HelloGroovy.groovy,得到结果

hello groovy!

是不是很简单,很像脚本语言。

2 Groovy中的字符串

Groovy 对字符串支持相当强大,可以使用多种字符串,包括单引号、双引号、三引号。

String str='hello groovy!'

String getStr(){
    'hello groovy!'
}

String str1='hello'
String str2=' groovy!'
println 'hello groovy!'
println "hello groovy!"
println "$str"
println getStr()
println "${str1+str2}"
println 'hello $str2'  //单引号,直接打印内容

使用单引号会直接打印字符串的内容,不会对美元$符号的内容进行转义。使用双引号的功能更加强大,字符串中使用美元符$后面可以跟字符串变量,如"$str",也可以跟表达式如 "${str1+str2}",如果跟表达式,记得要用{}括起来。

三引号用的比较少,可以支持字符串内容换行,我们了解一下就行。

println ''' aa
bb

cc
'''

我们在getStr()方法中并没有使用return语句,这是因为Groovy默认是把最后一行的执行结果进行返回。如果你需要返回的结果正好是最后一行,就可以省略return。

另外,在Groovy中,语句的最后的“;”号是可以省略的。还有一点就是Groovy中在调用函数的时候可以不加括号,比如,println ("hello")println "hello" 的意思是一样的

3 def关键字

def关键字很像是Java中的Object,在定义变量或者方法的时候使用def,如果没有指定具体的参数类型。在运行阶段Groovy会自动判断参数类型。

def a = 1, b = 2
def str = "hello"
println a + b
println str

实际上我们在定义变量的时候是可以省略def的,如

a = 1
b = 2
str = "hello"

但是,最好还是加上def,这是一种好的实践。
你还可以在定义变量的时候,在def后面再加上具体的参数类型,如

def int a = 1
def String str = "hello"

使用def定义方法的返回值类型,可以返回任意类型。如果指定了具体的返回值类型,就要正确的返回,否则会报错。

4 集合

Groovy的基本类型与Java一样。对于集合类型的数据,List的具体实现是ArrayList。Map集合的具体实现是LinkedHashMap。

4.1 List

下面我们来简单了解一下如果使用。List使用一个 []中括号来括起来来表示的。

//定义一个List
def lists=['groovy','gradle','android']

//打印集合size
println lists.size

//打印集合中的数据
lists.each{
    list->println list
}

取出集合中的数据直接用索引就可以了。比如:

assert lists[0]=='groovy'

对于集合中变量的存储,我们是不需要担心数组越界的,如果索引超过当前长度,List会自动在该索引中添加元素。比如

lists[100]=100
println lists.size
println lists[99]

打印结果,会发现集合的size变为101。index为99的集合数据为null。

4.2 Map

Map的表示就是用中括号[]括起来的 key:value形式。

def map=["key1":"hello","key2":"groovy","key3":true]
println map.size()

map.forEach{
    key,value->
    println "$key :$value"
}

结果是:

3
key1 :hello
key2 :groovy
key3 :true

获取Map的元素

println map.'key1'
println map['key1']

添加元素

map.'key4'='android'
println map['key4']

5 Groovy的类

Groovy中的类与Java中的类的写法类似,但是要更简洁一些。
我们在Hello.groovy文件中写一个HelloWorld类。

class HelloWorld{
    String msg='hello world'
    
    HelloWorld(){
        
    }

    HelloWorld(String msg){
        this.msg=msg
    }

    void sayHello(){
        println msg
    }
}

def say={
    new HelloWorld('hello').sayHello()
}

def getMsg={
    new HelloWorld().msg
}

say()
println getMsg()

调用groovy Hello.groovy 命令执行该脚本,结果如下:

hello
hello world

当然了,我们也可以写一个单独的HelloWorld类,带上包名,然后在其它地方导包使用,就像Java那样。有关于Groovy类的使用,我们后面还会讲到,这里就不赘述了。
总结一下Groovy的类相比Java的一些不同,或者说是优化的一些地方:

  • 表达式后面的分号是可选的
  • 每个类、构造方法和方法默认是public的
  • 在Groovy中,方法体中的最后一个表达式的值会被作为返回值。这意味着return语句是可选的
  • Groovy编译器自动加上getter/setter方法,所以不需要自己去书写
  • 类的属性可以通过点号来获取,看起来好像它们在Java中是public的,在底层Groovy调用的是自动生成的getter/setter方法。

6 闭包

闭包Closure,在Groovy中是很重要的一种数据类型。闭包实际上就是一段代码块,需要用{}包括起来。前面我们在讲解一些例子的时候已经用到了闭包,我们再来看一下闭包的结构。

//定义一个闭包 ,记得要用{}包裹起来
def aClosure={
    int a,int b-> //-> 箭头前面代表的是参数,后面是执行语句
    a+b  //返回值
}

println aClosure(1,2)

结果为 3。

闭包的参数类型也可以不指定,在运行期有Groovy自动推断,比如下面这个例子,执行起来也是没有问题的

def bClosure={
     a,b->
    a+b
}

println bClosure(1,2)

闭包也可以没有参数
如:

def aa={
    println "this is a Closure"
}
aa()

总结一下,闭包的类型有

1. def closureNama ={params -> code} 
2. def closureName= {code} 没有参数的时候就没有箭头 ->

闭包的调用有两种方式

1. 闭包对象.call(参数)
2. 闭包对象(参数)

如
aClosure(1,2)
aClosure.call(1,2)

需要注意一点,闭包如果没有参数的话,其隐含了一个参数是 it
和this的作用类似,代表的是传入闭包的参数。比如:

def sayHello={"hello ${it}"}
println sayHello("Jim")

等同于
def sayHello={it->"hello ${it}"}

当然了,如果闭包显示的指明了无参数,则在调用闭包的时候不能传参数,否则会报错。如

def noParams={->println "noParams"}
noParams();
//下面的代码执行会报错
//noParams(1)

闭包返回值
闭包总是会返回一个值。返回值是闭包的最后一条语句的值(如果没有显式的return语句),或者是可执行的return 语句的值。如果闭包的最后一条语句没有值就返回null。如之前举得Hello.groovy中的例子:

def getMsg={
    new HelloWorld().msg
}

闭包作为方法参数
闭包也可以作为方法参数,如:

int increment(Closure closure,int count){
    closure()+count
}
//断言,如果为true就正常执行,如果为false,就会报错
assert increment({1+1},1)==3

闭包委托
闭包代码是在委托的闭包上执行。默认的,这个委托就是闭包的所有者。比如你在Groovy脚本中定义了一个闭包,那么所有者就是一个groovy.lang.Script实例。闭包的隐式变量delegate 允许你重新定义默认的所有者。

例如:

class Test {
    def x = 30
    def y = 40

    def run() {
        def data = [ x: 10, y: 20 ]
        def cl = { 
            y = x + y 
        }
        cl.delegate = data
        cl.resolveStrategy = Closure.DELEGATE_FIRST
        cl()
        assert x == 30
        assert y == 40
        assert data == [x:10, y:30]
    }
}

new Test().run()

上面例子中闭包c1的委托变为data,闭包的resolveStrategy在默认情况下是OWNER_FIRST,即它会先查找闭包的owner(在本例中指的就是Test对象本身),如果owner存在,则在owner上执行闭包中的代码。这里我们将其设置成了DELEGATE_FIRST,即该闭包会首先查找delegate(本例中即data),如果找不到再去找owner。resolveStrategy还有其它的一些情况,具体的可以查看文档中的例子,相信当你看到这些例子后,会对闭包委托有一个清晰的认识。

7 总结

限于篇幅和本系列的主题,本文简单介绍了Groovy的一些语法和数据结构,这对于学习Gradle会有一些帮助。在后面Gradle的学习过程中,如果对Groovy的一些Api不熟悉可以查看Groovy的Api文档,我们没必要死记硬背这个Api,掌握学习的方法更加重要,对吗?

下一篇,我们就正式进入Gradle部分的学习了。

参考

推荐阅读更多精彩内容