Android Gradle

Android Gradle(一)——Groovy基础

2018-07-30  本文已影响50人  蓝十一丶

一、Groovy概述

Groovy是基于JVM的一种动态语言,它结合了Python、Ruby和Smalltalk的特性,同时能与Java代码很好的结合,用于扩展现在的代码,具有以下特点:


二、Groovy语法

Groovy完全兼容Java,所以在Groovy文件中可以写任何的Java代码来进行编程,这里主要是讲Groovy不同于Java的语法特性(在Groovy中所有的代码都不必要用;结尾):

1.变量

Groovy具有动态类型转换的特点,所以类型对于变量,属性,方法,闭包参数以及函数的返回类型都是可有可无的,在给变量赋值时才确定它的类型,并且在需要时,很多类型之间的转换都可以自动发生。这里要注意的是Groovy可以直接编译成class字节码,JVM是无法判断字节码是Java生成的还是Groovy生成的。除了基本类型外,Groovy中的变量类型都是对象
下面是一些常见变量的声明的例子

字符串

def str1 = '单引号' //Groovy中的字符串可以用单引号来表示
def str2 = "双引号" //也可以用双引号表示

Groovy中的单引号声明的字符串中不具有运算能力,即不能包含运算表达式,而双引号可以通过${str1}的方式来进行运算

println '单引号的变量运算:${str1}'
println "双引号的变量运算:${str2}" //只有双引号才能运算
println "运算表达式结果为:${str1+str2}" //${}中可以进行运算

输出为:

单引号的变量运算:${str1}
双引号的变量运算:双引号
运算表达式结果为:单引号双引号

集合

Groovy兼容了Java中的集合,并进行了扩展,如Groovy中的集合中的元素允许使用不同的类型。注意,在Groovy中定义的集合是Java中对应类的对象,可以使用任何Java库提供的方法,如size(),add(E e)等方法。这里主要讲常见的List和Map

List

Groovy中的List无需实现List接口,声明如下:

def numList = [1,2,3,4,5,6]
def mixList = ['1','2',"start",4,5,6] //Groovy可以自动进行动态类型转换

默认的List是一个ArrayList类型,比如我们可以通过如下方式查看它的类型:

println numList.getClass().name //输出为:java.util.ArrayList

Groovy访问List的方式与Java不同,无需通过get方法,而是直接使用下标来进行访问的:

println mixList //输出整个List,为:[1, 2, start, 4, 5, 6]
println numList[0] //访问第一个元素
println numList[1] //访问第二个元素
println numList[-1] //访问倒数第一个元素
println numList[-2] //访问倒数第二个元素
println numList[1..3] //访问第二个到第四个元素,输出为:[2, 3, 4]
println numList[1..3].getClass().name //java.util.ArrayList

Groovy中List还提供了each方法传入闭包来进行迭代操作

numList.each {
    println it
}

输出为:

1
2
3
4
5
6

Map

Map的用法类似于List,区别只是Map是通过键值对的方式来声明的:

def map = ['name':'Mike', 'age':20]
println map.getClass().name //java.util.LinkedHashMap

Groovy中的Map默认是LinkedHashMap类型,访问可以通过map[key]或者map.key来进行访问:

println map['name'] //输出为:Mike
println map.age //输出为:20

Map也有与List类似的迭代操作:

map.each {
    println "Key:${it.key}, Value:${it.value}"
}

输出为:

Key:name, Value:Mike
Key:age, Value:20

2.函数

函数与方法的区别在于函数式独立于类外的,而方法是类中的方法,是一种特殊的函数,两者基本语法相同,方法多了一些类的特性和访问权限而已
Groovy中的函数与Java中的不同点如下:

例1:

fun1(10, 11) //输出为:21
fun1 8,9 //输出为:17
def fun1(int a, int b) {
    println a+b
}

例2:

println fun1(10, 11) //输出为:21
//println fun1 8,9 这是错误的写法,会报错
println fun1('abc','def') //输出为:abcdef
def fun1(a, b) {
    a
    b
    a+b
}

3.类

在Groovy类中默认修饰符是public,没有default修饰符。
Groovy会自动给类中的变量设置setter和getter方法(你当然可以自己手动重写它们),我们可以直接通过类名.成员来调用这些方法来对类中的成员变量进行访问和修改。(请注意Groovy自动设置的setter和getter不能通过方法名去调用它们,如果你确实要这样做,请在类中自定义这些方法)

Person p = new Person()

println "名字为:${p.name}" //默认为null,输出为:名字为:null
println p.i //默认为0,输出为:0
println p.b //默认为false,输出为:false
p.name = 'Mike'
println "名字为:${p.name}" //输出为:名字为:Mike

class Person {
    private String name
    private boolean b
    private int i
}

你也可以手动重写getter方法:

Person p = new Person()

println "名字为:${p.name}" //输出为:名字为:abc
p.name = 'Mike'
println "名字为:${p.name}" //输出为:名字为:abc

class Person {
    private String name

    public String getName() {
        return "abc"
    }
}

在类中可以直接通过成员名访问从父类继承来的私有成员,但是这种情况父类必须自己定义getter/setter方法

class C1 {
    private int x = 5
    //必须包含这个方法,不然子类无法直接使用x
    int getX() {
        return x
    }
}

class C2 extends C1 {
    public test() {
        println x
    }
}

new C2().test() //5

.运算符并不一定是操作类的成员变量的,其实质只是调用了setter/getter方法而已:

User user = new User()
println user.age //输出为:12
//user.age = 10 User中没有定义setAge方法,所以会报错

class User {
    public int getAge() {
        12
    }
}

在Gradle中有很多类似于这样的用法,其实类中并没有定义对应的属性,只不过是定义了相应的getter/setter方法而已

三、Groovy闭包

闭包是Groovy非常重要的一个特性,也是DSL的基础。闭包使Groovy省去了Java匿名内部类的繁琐,使代码变的灵活,轻量,以及可复用。

<font face="微软雅黑" size = 4>闭包实质就是可以用作函数参数和方法参数的代码块,可以将这个代码块理解为一个函数指针</font>

定义闭包

闭包在Groovy中是groov.lang.Closure类,可以用Closure来声明,也可以用def来声明
Groovy中闭包的定义如下:

def xxx = {[params -> ] code} //记住 -> 是连在一起的,不能分开

params可以理解为是函数的形参,形参名不能和外部变量同名,如果只有一个参数,可以将不指定参数,Groovy会指定一个隐式的默认参数it,如果没有参数,也可以不指定参数:

Closure closure1 = {
    println it
}

Closure closure2 = {
    k,v ->
        println "${k} is ${v}"
}

调用闭包

闭包可以直接当做一段代码来调用,通过闭包.call(参数)或者闭包(参数)来调用,也可以作为参数传递到函数中在调用。闭包跟方法一样,可以省略括号,用空格代替,当然无参闭包必须带上括号,否则会输出闭包的类对象

closure1.call('Mike') //Mike
closure2('name', 'Mike') //name is Mike
closure2 'name', 'Mike' //name is Mike
closure3.call() //There is no parameter

需要注意的是,闭包是可以访问外部变量的,而函数不行

String name = 'Jack'
Closure closure4 = {
    println name
}
closure4() //Jack

闭包跟函数一样,是有返回值的,默认最后一行语句是闭包的返回值,如果最后一行语句没有返回类型,那么返回null(比如定义变量或者直接给出常量,就是有返回值)

//定义一个有返回值的闭包
Closure closure5 = {
    'hello world'
}
//打印两个闭包的返回值,closure4最后一句是没有返回值的
println closure4() //null
println closure5() //hello world

闭包可以视为一个普通的变量,所以闭包可以作为参数传递给函数,或者另一个闭包,也可以作为闭包的返回值返回

def run1 = {a -> a * a}
def run2 = {x, y -> y(x)}
def run3 = {m -> {n -> m * n}}
println run3(3)(run2(5,run1)) //输出为:75

分析上段代码:

  1. 闭包run3传入了参数3,即m = 3,并返回一个闭包{n-> 3*n}
  2. 返回的闭包继续传入参数run2(5,run1),即n = run2(5,run1),返回一个数值为3*run2(5,run1)
  3. 继续看run2(5,run1),run2接受参数5和run1,返回值为run1(5),即为25
  4. 所以结果为75

对于闭包有一些省略的写法:

//之前的代码可以写成如下形式:
println run3(3)(run2(5){a -> a * a}) //将run1从run2参数列表中拉出

再看下List的each方法的写法:
numList.each {println it}
实质是List.each(closure),即真正写法是这样的:
numList.each({println it})
然后将闭包从参数列表中拉出:
numList.each(){println it}
在根据函数和闭包的参数写法可以省略括号,用空格代替:
numList.each {println it}

闭包的参数可以接受Map和List:

def x = {a, b, c -> a.x * a.y + b + c}
println x(5, 7, x:2, y:3) //18
def c = {a, b ,c -> a * b + c}
def list = [1,2,3]
println c(list) //5

闭包委托

委托策略是Groovy闭包中独有的语法,Groovy通过闭包委托策略使得DSL语言更加优化和简洁,在Gradle中就大量使用了闭包的委托策略。

在抽象类groovy.lang.Closure中包含了三个成员变量:

  private Object delegate;
  private Object owner;
  private Object thisObject;

三个成员含义如下:

根据前面讲的Groovy中类中成员的访问方式,Closure是抽象类,其中定义了对应的getter,delegate还设置了对应的setter方法。我们定义的都是它的实现子类,可以在闭包中通过三种成员名来直接访问这三种成员。

class C1 {
    def firstClosure = {
        println "firstClosure.thisObject:${thisObject.getClass()}"
        println "firstClosure.owner:${owner.getClass()}"
        println "firstClosure.delegate:${delegate.getClass()}"

        def secondClosure = {
            println "secondClosure.thisObject:${thisObject.getClass()}"
            println "secondClosure.owner:${owner.getClass()}"
            println "secondClosure.delegate:${delegate.getClass()}"
        }
        secondClosure()
        new C2().test {
            println "test.thisObject:${thisObject.getClass()}"
            println "test.owner:${owner.getClass()}"
            println "test.delegate:${delegate.getClass()}"
        }
    }
}

class C2 {
    def test(Closure closure) {
        closure()
    }
}
new C1().firstClosure()

输出如下:

firstClosure.thisObject:class C1
firstClosure.owner:class C1
firstClosure.delegate:class C1
secondClosure.thisObject:class C1
secondClosure.owner:class C1$_closure1
secondClosure.delegate:class C1$_closure1
test.thisObject:class C1
test.owner:class C1$_closure1
test.delegate:class C1$_closure1

Delegate策略

当闭包中出现一个属性没有指定其所有者时,就会执行对应的Delegate策略:

看如下的例子:

class User {
    String name

    def c1 = {
        //def name = 100 
        println name
    }

}

class Person {
    String name

    def test(Closure c) {
        c()
    }
}
String name = 'Wrapper'
User u = new User()
Person p = new Person()
u.name = 'user'
p.name = 'person'
p.test(u.c1) //user
u.c1.setResolveStrategy(Closure.DELEGATE_FIRST)
u.c1.delegate = p
p.test(u.c1) //person
p.test {
    println name
} //Wrapper

上述代码,如果在闭包中设置了name = 100,那么闭包调用时不会调用委托策略,而是直接输出100
第一次输出user是因为默认从owner中找name属性
第二次输出person是因为设置了优先从delegate中寻找name属性
第三次输出Wrapper是因为匿名闭包的owner是外部的类,而非其调用者,所以输出Wrapper

在Gradle中基本上都是用Delegate策略使用闭包来对项目进行配置属性的

ps:第一次写博客,请多多指教!

上一篇下一篇

猜你喜欢

热点阅读