Android Gradle(一)——Groovy基礎(chǔ)

一神帅、Groovy概述

Groovy是基于JVM的一種動(dòng)態(tài)語(yǔ)言罕容,它結(jié)合了Python忘渔、Ruby和Smalltalk的特性高帖,同時(shí)能與Java代碼很好的結(jié)合,用于擴(kuò)展現(xiàn)在的代碼畦粮,具有以下特點(diǎn):

  • 具有動(dòng)態(tài)語(yǔ)言的特點(diǎn)散址,如動(dòng)態(tài)類型轉(zhuǎn)換、閉包和元編程
  • 面向?qū)ο缶幊绦猓瑫r(shí)也可以作為腳本語(yǔ)言
  • 直接編譯成Java字節(jié)碼预麸,在任何使用Java的地方都可以使用Groovy
  • Groovy完全兼容Java語(yǔ)言,無(wú)縫集成所有Java已有的庫(kù)和對(duì)象
  • 支持函數(shù)式編程儒将,無(wú)需main函數(shù)
  • 支持DSL吏祸,gradle自動(dòng)化構(gòu)建系統(tǒng)就是用Groovy語(yǔ)言實(shí)現(xiàn)的

二、Groovy語(yǔ)法

Groovy完全兼容Java钩蚊,所以在Groovy文件中可以寫(xiě)任何的Java代碼來(lái)進(jìn)行編程贡翘,這里主要是講Groovy不同于Java的語(yǔ)法特性(在Groovy中所有的代碼都不必要用;結(jié)尾):

1.變量

Groovy具有動(dòng)態(tài)類型轉(zhuǎn)換的特點(diǎn),所以類型對(duì)于變量砰逻,屬性鸣驱,方法,閉包參數(shù)以及函數(shù)的返回類型都是可有可無(wú)的诱渤,在給變量賦值時(shí)才確定它的類型丐巫,并且在需要時(shí),很多類型之間的轉(zhuǎn)換都可以自動(dòng)發(fā)生勺美。這里要注意的是Groovy可以直接編譯成class字節(jié)碼递胧,JVM是無(wú)法判斷字節(jié)碼是Java生成的還是Groovy生成的。除了基本類型外赡茸,Groovy中的變量類型都是對(duì)象
下面是一些常見(jiàn)變量的聲明的例子

字符串

def str1 = '單引號(hào)' //Groovy中的字符串可以用單引號(hào)來(lái)表示
def str2 = "雙引號(hào)" //也可以用雙引號(hào)表示

Groovy中的單引號(hào)聲明的字符串中不具有運(yùn)算能力缎脾,即不能包含運(yùn)算表達(dá)式,而雙引號(hào)可以通過(guò)${str1}的方式來(lái)進(jìn)行運(yùn)算

println '單引號(hào)的變量運(yùn)算:${str1}'
println "雙引號(hào)的變量運(yùn)算:${str2}" //只有雙引號(hào)才能運(yùn)算
println "運(yùn)算表達(dá)式結(jié)果為:${str1+str2}" //${}中可以進(jìn)行運(yùn)算

輸出為:

單引號(hào)的變量運(yùn)算:${str1}
雙引號(hào)的變量運(yùn)算:雙引號(hào)
運(yùn)算表達(dá)式結(jié)果為:單引號(hào)雙引號(hào)

集合

Groovy兼容了Java中的集合占卧,并進(jìn)行了擴(kuò)展遗菠,如Groovy中的集合中的元素允許使用不同的類型联喘。注意,在Groovy中定義的集合是Java中對(duì)應(yīng)類的對(duì)象辙纬,可以使用任何Java庫(kù)提供的方法豁遭,如size(),add(E e)等方法。這里主要講常見(jiàn)的List和Map

List

Groovy中的List無(wú)需實(shí)現(xiàn)List接口贺拣,聲明如下:

def numList = [1,2,3,4,5,6]
def mixList = ['1','2',"start",4,5,6] //Groovy可以自動(dòng)進(jìn)行動(dòng)態(tài)類型轉(zhuǎn)換

默認(rèn)的List是一個(gè)ArrayList類型蓖谢,比如我們可以通過(guò)如下方式查看它的類型:

println numList.getClass().name //輸出為:java.util.ArrayList

Groovy訪問(wèn)List的方式與Java不同,無(wú)需通過(guò)get方法譬涡,而是直接使用下標(biāo)來(lái)進(jìn)行訪問(wèn)的:

println mixList //輸出整個(gè)List闪幽,為:[1, 2, start, 4, 5, 6]
println numList[0] //訪問(wèn)第一個(gè)元素
println numList[1] //訪問(wèn)第二個(gè)元素
println numList[-1] //訪問(wèn)倒數(shù)第一個(gè)元素
println numList[-2] //訪問(wèn)倒數(shù)第二個(gè)元素
println numList[1..3] //訪問(wèn)第二個(gè)到第四個(gè)元素,輸出為:[2, 3, 4]
println numList[1..3].getClass().name //java.util.ArrayList

Groovy中List還提供了each方法傳入閉包來(lái)進(jìn)行迭代操作

numList.each {
    println it
}

輸出為:

1
2
3
4
5
6

Map

Map的用法類似于List涡匀,區(qū)別只是Map是通過(guò)鍵值對(duì)的方式來(lái)聲明的:

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

Groovy中的Map默認(rèn)是LinkedHashMap類型盯腌,訪問(wèn)可以通過(guò)map[key]或者map.key來(lái)進(jìn)行訪問(wèn):

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.函數(shù)

函數(shù)與方法的區(qū)別在于函數(shù)式獨(dú)立于類外的,而方法是類中的方法陨瘩,是一種特殊的函數(shù)腕够,兩者基本語(yǔ)法相同,方法多了一些類的特性和訪問(wèn)權(quán)限而已
Groovy中的函數(shù)與Java中的不同點(diǎn)如下:

  • 可以使用def來(lái)定義舌劳,定義形參時(shí)可以不用顯示定義類型燕少,Groovy會(huì)自動(dòng)進(jìn)行動(dòng)態(tài)類型轉(zhuǎn)換
  • 參數(shù)外的括號(hào)可以省略,函數(shù)名和參數(shù)列表之間用空格隔開(kāi)蒿囤,無(wú)參函數(shù)必須帶括號(hào)
  • 函數(shù)嵌套在同一行調(diào)用時(shí),只有第一個(gè)函數(shù)可以省略括號(hào)
  • 無(wú)需return語(yǔ)句崇决,Groovy會(huì)自動(dòng)將函數(shù)中真正執(zhí)行的最后一句代碼作為其返回值材诽,如果使用void修飾函數(shù),函數(shù)會(huì)返回null(比如定義變量或者直接給出常量恒傻,就是有返回值脸侥,調(diào)用無(wú)返回值的函數(shù),如println盈厘,就是無(wú)返回值)

例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 這是錯(cuò)誤的寫(xiě)法睁枕,會(huì)報(bào)錯(cuò)
println fun1('abc','def') //輸出為:abcdef
def fun1(a, b) {
    a
    b
    a+b
}

3.類

在Groovy類中默認(rèn)修飾符是public,沒(méi)有default修飾符沸手。
Groovy會(huì)自動(dòng)給類中的變量設(shè)置setter和getter方法(你當(dāng)然可以自己手動(dòng)重寫(xiě)它們)外遇,我們可以直接通過(guò)類名.成員來(lái)調(diào)用這些方法來(lái)對(duì)類中的成員變量進(jìn)行訪問(wèn)和修改。(請(qǐng)注意Groovy自動(dòng)設(shè)置的setter和getter不能通過(guò)方法名去調(diào)用它們契吉,如果你確實(shí)要這樣做跳仿,請(qǐng)?jiān)陬愔凶远x這些方法)

Person p = new Person()

println "名字為:${p.name}" //默認(rèn)為null,輸出為:名字為:null
println p.i //默認(rèn)為0捐晶,輸出為:0
println p.b //默認(rèn)為false菲语,輸出為:false
p.name = 'Mike'
println "名字為:${p.name}" //輸出為:名字為:Mike

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

你也可以手動(dòng)重寫(xiě)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"
    }
}

在類中可以直接通過(guò)成員名訪問(wèn)從父類繼承來(lái)的私有成員妄辩,但是這種情況父類必須自己定義getter/setter方法

class C1 {
    private int x = 5
    //必須包含這個(gè)方法,不然子類無(wú)法直接使用x
    int getX() {
        return x
    }
}

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

new C2().test() //5

.運(yùn)算符并不一定是操作類的成員變量的山上,其實(shí)質(zhì)只是調(diào)用了setter/getter方法而已:

User user = new User()
println user.age //輸出為:12
//user.age = 10 User中沒(méi)有定義setAge方法眼耀,所以會(huì)報(bào)錯(cuò)

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

在Gradle中有很多類似于這樣的用法,其實(shí)類中并沒(méi)有定義對(duì)應(yīng)的屬性佩憾,只不過(guò)是定義了相應(yīng)的getter/setter方法而已

三哮伟、Groovy閉包

閉包是Groovy非常重要的一個(gè)特性,也是DSL的基礎(chǔ)鸯屿。閉包使Groovy省去了Java匿名內(nèi)部類的繁瑣澈吨,使代碼變的靈活,輕量寄摆,以及可復(fù)用谅辣。

<font face="微軟雅黑" size = 4>閉包實(shí)質(zhì)就是可以用作函數(shù)參數(shù)和方法參數(shù)的代碼塊,可以將這個(gè)代碼塊理解為一個(gè)函數(shù)指針</font>

定義閉包

閉包在Groovy中是groov.lang.Closure類婶恼,可以用Closure來(lái)聲明桑阶,也可以用def來(lái)聲明
Groovy中閉包的定義如下:

def xxx = {[params -> ] code} //記住 -> 是連在一起的,不能分開(kāi)

params可以理解為是函數(shù)的形參勾邦,形參名不能和外部變量同名蚣录,如果只有一個(gè)參數(shù),可以將不指定參數(shù)眷篇,Groovy會(huì)指定一個(gè)隱式的默認(rèn)參數(shù)it萎河,如果沒(méi)有參數(shù),也可以不指定參數(shù):

Closure closure1 = {
    println it
}

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

調(diào)用閉包

閉包可以直接當(dāng)做一段代碼來(lái)調(diào)用蕉饼,通過(guò)閉包.call(參數(shù))或者閉包(參數(shù))來(lái)調(diào)用虐杯,也可以作為參數(shù)傳遞到函數(shù)中在調(diào)用。閉包跟方法一樣昧港,可以省略括號(hào)擎椰,用空格代替,當(dāng)然無(wú)參閉包必須帶上括號(hào)创肥,否則會(huì)輸出閉包的類對(duì)象

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

需要注意的是达舒,閉包是可以訪問(wèn)外部變量的,而函數(shù)不行

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

閉包跟函數(shù)一樣叹侄,是有返回值的巩搏,默認(rèn)最后一行語(yǔ)句是閉包的返回值,如果最后一行語(yǔ)句沒(méi)有返回類型趾代,那么返回null(比如定義變量或者直接給出常量塔猾,就是有返回值)

//定義一個(gè)有返回值的閉包
Closure closure5 = {
    'hello world'
}
//打印兩個(gè)閉包的返回值,closure4最后一句是沒(méi)有返回值的
println closure4() //null
println closure5() //hello world

閉包可以視為一個(gè)普通的變量,所以閉包可以作為參數(shù)傳遞給函數(shù)稽坤,或者另一個(gè)閉包丈甸,也可以作為閉包的返回值返回

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傳入了參數(shù)3糯俗,即m = 3,并返回一個(gè)閉包{n-> 3*n}
  2. 返回的閉包繼續(xù)傳入?yún)?shù)run2(5,run1)睦擂,即n = run2(5,run1)得湘,返回一個(gè)數(shù)值為3*run2(5,run1)
  3. 繼續(xù)看run2(5,run1),run2接受參數(shù)5和run1顿仇,返回值為run1(5)淘正,即為25
  4. 所以結(jié)果為75

對(duì)于閉包有一些省略的寫(xiě)法:

  • 當(dāng)匿名閉包作為參數(shù)時(shí),如果它是最后一個(gè)參數(shù)臼闻,或者是唯一的參數(shù)鸿吆,可以將閉包從括號(hào)中拉出來(lái),放在括號(hào)后面:
//之前的代碼可以寫(xiě)成如下形式:
println run3(3)(run2(5){a -> a * a}) //將run1從run2參數(shù)列表中拉出

再看下List的each方法的寫(xiě)法:
numList.each {println it}
實(shí)質(zhì)是List.each(closure)述呐,即真正寫(xiě)法是這樣的:
numList.each({println it})
然后將閉包從參數(shù)列表中拉出:
numList.each(){println it}
在根據(jù)函數(shù)和閉包的參數(shù)寫(xiě)法可以省略括號(hào)惩淳,用空格代替:
numList.each {println it}

閉包的參數(shù)可以接受Map和List:

  • 閉包參數(shù)中與鍵值關(guān)系有關(guān)的參數(shù),會(huì)自動(dòng)封裝起來(lái)放到閉包的第一個(gè)參數(shù)
def x = {a, b, c -> a.x * a.y + b + c}
println x(5, 7, x:2, y:3) //18
  • 如果閉包參數(shù)列表中本身沒(méi)有List乓搬,那么傳入List會(huì)將List中的元素依次匹配到參數(shù)列表
def c = {a, b ,c -> a * b + c}
def list = [1,2,3]
println c(list) //5

閉包委托

委托策略是Groovy閉包中獨(dú)有的語(yǔ)法思犁,Groovy通過(guò)閉包委托策略使得DSL語(yǔ)言更加優(yōu)化和簡(jiǎn)潔,在Gradle中就大量使用了閉包的委托策略进肯。

在抽象類groovy.lang.Closure中包含了三個(gè)成員變量:

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

三個(gè)成員含義如下:

  • thisObject指閉包所在的類激蹲,注意是類對(duì)象,并且是在閉包定義時(shí)的外圍類對(duì)象江掩,匿名閉包的thisObject并不是其調(diào)用者
  • owner指閉包上層的對(duì)象学辱,即包含閉包的類或者閉包(多層嵌套的情況下)
  • delegate默認(rèn)為owner

根據(jù)前面講的Groovy中類中成員的訪問(wèn)方式,Closure是抽象類环形,其中定義了對(duì)應(yīng)的getter项郊,delegate還設(shè)置了對(duì)應(yīng)的setter方法。我們定義的都是它的實(shí)現(xiàn)子類斟赚,可以在閉包中通過(guò)三種成員名來(lái)直接訪問(wèn)這三種成員。

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策略

當(dāng)閉包中出現(xiàn)一個(gè)屬性沒(méi)有指定其所有者時(shí)差油,就會(huì)執(zhí)行對(duì)應(yīng)的Delegate策略:

  • OWNER_FIRST:這是默認(rèn)的策略拗军,會(huì)優(yōu)先從owner中尋找對(duì)應(yīng)的屬性,如果找不到會(huì)去delegate中找
  • DELEGATE_FIRST:與OWNER_FIRST相反蓄喇。
  • OWNER_ONLY:只在owner中尋找
  • DELEGATE_ONLY:只在delegate中尋找
  • TO_SELF:只在閉包自身中找

看如下的例子:

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

上述代碼发侵,如果在閉包中設(shè)置了name = 100,那么閉包調(diào)用時(shí)不會(huì)調(diào)用委托策略妆偏,而是直接輸出100
第一次輸出user是因?yàn)槟J(rèn)從owner中找name屬性
第二次輸出person是因?yàn)樵O(shè)置了優(yōu)先從delegate中尋找name屬性
第三次輸出Wrapper是因?yàn)槟涿]包的owner是外部的類刃鳄,而非其調(diào)用者,所以輸出Wrapper

在Gradle中基本上都是用Delegate策略使用閉包來(lái)對(duì)項(xiàng)目進(jìn)行配置屬性的

ps:第一次寫(xiě)博客钱骂,請(qǐng)多多指教叔锐!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挪鹏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子愉烙,更是在濱河造成了極大的恐慌讨盒,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件步责,死亡現(xiàn)場(chǎng)離奇詭異返顺,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蔓肯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門遂鹊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蔗包,你說(shuō)我怎么就攤上這事秉扑。” “怎么了气忠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵邻储,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我旧噪,道長(zhǎng)吨娜,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任淘钟,我火速辦了婚禮宦赠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘米母。我一直安慰自己勾扭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布铁瞒。 她就那樣靜靜地躺著妙色,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慧耍。 梳的紋絲不亂的頭發(fā)上身辨,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音芍碧,去河邊找鬼煌珊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泌豆,可吹牛的內(nèi)容都是我干的定庵。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蔬浙!你這毒婦竟也來(lái)了猪落?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤敛滋,失蹤者是張志新(化名)和其女友劉穎许布,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體绎晃,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜜唾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了庶艾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袁余。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖咱揍,靈堂內(nèi)的尸體忽然破棺而出颖榜,到底是詐尸還是另有隱情,我是刑警寧澤煤裙,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布掩完,位于F島的核電站,受9級(jí)特大地震影響硼砰,放射性物質(zhì)發(fā)生泄漏且蓬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一题翰、第九天 我趴在偏房一處隱蔽的房頂上張望恶阴。 院中可真熱鬧,春花似錦豹障、人聲如沸冯事。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)昵仅。三九已至,卻和暖如春累魔,著一層夾襖步出監(jiān)牢的瞬間摔笤,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工薛夜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人版述。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓梯澜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子晚伙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348