Gradle入門(一)--Groovy常用語法

概述

Groovy是一種可以用於構建的DSL,基於Jvm支持所有的Java語法,同時又對Java進行了擴展,提供了大量的語法糖去簡化我們的代碼。在開發中既能像Java一樣用面向對象的方式編寫,也能像腳本一樣用面向過程的方式編寫。而本篇我們主要介紹Groovy在Gradle腳本中常用的語法,如果你想知道更多可以到官網查看http://groovy-lang.org/

在開始之前你需要裝好Java和Groovy環境,並配置好Intellij編譯器。

前言

我們先解釋下啥叫Groovy既能像Java一樣用面向對象的方式編寫,也能像腳本一樣用面向過程的方式編寫。

先看Groovy面向對象的方式

class Test {

    static void main(String[] args) {
        println "do something"
    }
}

執行main方法會打印出do something,和Java一樣編寫一個個的類去完成代碼。

在看看Groovy面向過程的方式

println "do something"

通過groovy命令執行該文件會打印出do something,是不是感覺很爽,有種所見即所得的感覺。

接下來我們更深一步看看這個腳本爲啥能執行,我們知道Groovy是基於Jvm的,那麼這段代碼肯定是編譯成了一個class去執行的,然後我們將class反編譯成java看看。

public class Test extends Script {
    public Test() {
        CallSite[] var1 = $getCallSiteArray();
        super();
    }

    public Test(Binding context) {
        CallSite[] var2 = $getCallSiteArray();
        super(context);
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].call(InvokerHelper.class, Test.class, args);
    }

    public Object run() {
        CallSite[] var1 = $getCallSiteArray();
        return var1[1].callCurrent(this, "do something");//打印do something
    }
}

你會發現雖然我們只寫一行println "do something"但它幫我們包裝成了一個類,真正執行是在run方法裏面幫我們打印了do something。

那麼稍作總結,Groovy既能像Java一樣用面向對象的方式編寫,也能像腳本一樣用面向過程的方式編寫,而腳本最終也是幫我們包裝成類去執行。

變量的定義

對於變量的定義和Java大體相同

int x = 10
double y = 3.14

稍有不同的是多了一種def定義方式,代表當前類型是可變的,根據使用的時候具體確定

def x = 1
x = 1.1
x = "abc"

方法的定義

大體和Java相同,修飾符默認是public

String test(){
    return "123"
}

不同的是可以通過def定義方法返回類型爲可變的

def test(){
    return "123"
}

還有點不同的是在有參方法調用的時候groovy可以省略大括號

println("123")
println "123"

除此之外還有和kt很像的特性,當閉包是方法最後一個參數的時候,閉包可以寫在括號外,而如果只有一個參數是閉包的話可以不用寫大括號,至於閉包我們後面會說到

def test1(String s, Closure closure) {

}
test1("1") {

}

def test2(Closure closure){
    
}
test2 {
    
}

String

定義有三種方式

def str1 = 'abc' //普通字符串
def str2 = '''
abc
def
''' //可以保留格式的字符串 比如換行
def str3 = "abc$str1" //可以使用佔位符的字符串

佔位符和kt完全一樣,在字符串中想使用變量的只需要$變量名,如果是使用表達式的話${表達式}

def str4 = "abc $str1"
def str5 = "abc: ${str1.length()}"

邏輯控制

先看最常用的for循環

def sum = 0
for (i in 0..9) {
    sum += i
}

/*對List循環*/
for (i in [1, 2, 3, 4, 5, 6, 7, 8, 9]) {
    sum += 1
}

/*對Map進行循環*/
for (i in ["lili": 1, "luck": 2, "xiaoming": 3]) {
    sum += i.value
}

至於list和map後面馬上會講,然後再是Groovy對switch做了很大的增強

def x = 1.23
def result
switch (x) {
    case "foo":
        result = "found foo"
        break
    case "bar":
        result = "bar"
        break
    case [1.23, 4, 5, 6, "inList"]://列表
        result = "list"
        break
    case 12..30://範圍
        result = "range"
        break
    case Integer:
        result = "int"
        break
    case BigDecimal:
        result = "bigDecimal"
        break
    default:
        result = "default"
        break
}

基本上你能想到的類型都可以作爲case判斷的條件

List

定義方式如下

def list = [-3, 9, 6, 2, -7, 1, 5]

你可能會問這不是和Java的數組衝突了,那要想定義一個數組怎麼搞呢

int[] array = [-3, 9, 6, 2, -7, 1, 5]
def array = [-3, 9, 6, 2, -7, 1, 5] as int[]

上面兩種方式都可以定義數組,但是其實沒有必要,數組的功能List都已經包含了,至於List的取值可以通過如下方式獲取元素

def list = [-3, 9, 6, 2, -7, 1, 5]
list[0]

Map

定義方式如下

def colors = [red: 'ff0000', green: '00ff00', blue: '0000ff']

有個需要注意的點是map的key全部都爲String,即使我這裏沒有顯示聲明爲String但都會默認轉爲String

至於獲取元素有兩種方式

colors['red']
colors.red

添加的話除了常用的put()也有簡化的方式

colors.yellow = 'ffff00'

Range

這個是Groovy特有的數據結構範圍,定義比較簡單

def range = 1..10

大部分時候都是用在for循環或者swtich的case中判斷在不在這個範圍

Closure

閉包就是一段封閉的代碼塊定義如下

def Closure = {
	
}

調用的話有兩種方式,我個人更傾向使用第二種方式

closure.call()
closure()

一般情況下閉包都是作爲方法參數而使用,以map的each方法來舉例

def map = [1: "aaa", 2: "bbb", 3: "ccc"]
map.each {
    println "key: $it.key,value: $it.value"
}

該方法會打印出map的key和value,不過對於上面這個你肯定會有個疑問it是啥,它是當閉包只有一個參數的時候我們可以不用顯式的聲明,默認會提供一個it變量代表那個唯一的參數。那麼你可能又會問那我怎麼知道閉包中的參數呢,這個就得看map的each方法了

    /**
     * Allows a Map to be iterated through using a closure. If the
     * closure takes one parameter then it will be passed the Map.Entry
     * otherwise if the closure takes two parameters then it will be
     * passed the key and the value.
     * <pre class="groovyTestCase">def result = ""
     * [a:1, b:3].each { key, value -> result += "$key$value" }
     * assert result == "a1b3"</pre>
     * <pre class="groovyTestCase">def result = ""
     * [a:1, b:3].each { entry -> result += entry }
     * assert result == "a=1b=3"</pre>
     *
     * In general, the order in which the map contents are processed
     * cannot be guaranteed. In practise, specialized forms of Map,
     * e.g. a TreeMap will have its contents processed according to
     * the natural ordering of the map.
     *
     * @param self    the map over which we iterate
     * @param closure the 1 or 2 arg closure applied on each entry of the map
     * @return returns the self parameter
     * @since 1.5.0
     */
    public static <K, V> Map<K, V> each(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure closure) {
        for (Map.Entry entry : self.entrySet()) {
            callClosureForMapEntry(closure, entry);
        }
        return self;
    }

註釋裏面可以看到closure支持1或者2個參數,那麼參數類型究竟是啥呢我們接着看callClosureForMapEntry()

    protected static <T, K, V> T callClosureForMapEntry(@ClosureParams(value=FromString.class, options={"K,V","Map.Entry<K,V>"}) Closure<T> closure, Map.Entry<K,V> entry) {
        if (closure.getMaximumNumberOfParameters() == 2) {
            return closure.call(entry.getKey(), entry.getValue());
        }
        return closure.call(entry);
    }

這裏就很明顯了當閉包爲兩個參數的時候,參數分別是key和value,爲一個參數的時候就是map的entry對象。所以當我們不清楚閉包的參數的時候,要麼去找文檔,要麼去查看源碼即可知道。

對於閉包的默認參數it需要着重說下,雖然它和kt的lambda表達式很像但是也有不同,即我們自己寫的閉包沒有定義參數它都會默認有個it參數,直接看例子

def closure = {
    println it
}
closure("abc")//會打印出abc

總結

上面介紹的是Groovy在寫Gradle腳本中常用的語法,當然Groovy語法並不止這麼點,也有很像kt一樣的擴展方法,委託等等一些高級用法不過在腳本中我們基本用不到也就不介紹了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章