概述
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一樣的擴展方法,委託等等一些高級用法不過在腳本中我們基本用不到也就不介紹了。