Gradle 之語言基礎 Groovy

李劍昆發表於2018-11-30

最近在學習 Android 中 Gradle 相關的知識,如果想學好 Gradle,必要的 Groovy 基礎是不可少的。Groovy 語言的知識也是非常多的,如果只是想在 Android Gradle 這個範圍內使用 Groovy,會 Groovy 的基礎即可,Groovy 語言文件

一. Android Gradle 概述

Groovy 是從 Java 衍生出來的,Groovy 的原始碼檔案 .groovy 也是編譯成為 .class 檔案,然後執行在 JVM 虛擬機器上的。其目標是,不管是作為指令碼語言,還是程式語言,都可以簡單、直接的使用。在 Android 的 Gradle 中,Groovy 更多的是用於編寫指令碼。如果會 Java,學習 Groovy 會比較簡單,甚至可以混寫 Java 和 Groovy 語法。

Groovy 相比 Java 語言來講,更加的方便靈活,更多的語法糖使編寫 Groovy 程式碼更加的簡潔,而且在 Groovy 中具有函數語言程式設計的思想。比如:Groovy 中非常重要的閉包 Closure 概念(類似於 C 語言中的函式指標),可以當做一個函式也執行,也可以當做某個函式的引數傳入到函式中去,也可以當做一個函式的返回值返回。

想學好 Gradle,除了必要的 Groovy 知識基礎以外,還需要了解其他兩個基礎知識:Android DSL 和 Gradle DSL。 DSL 是 Domain Specific Language(領域特定語言)的縮寫,其定義是:針對某一領域,具有受限表達性的一種計算機程式設計語言。學習 Android Gradle,Android DSL 和 Gradle DSL 也是需要學習的,好在有官方的文件 Android DSL 文件Gradle DSL 文件,學習起來就比較方便。在這篇文章中,不會過多地介紹 Android DSL 和 Gradle DSL,在下篇文章中會介紹。

好了,廢話不多說,接下來就來學習 Groovy 的語法基礎吧。為了學習的時候,可以執行 gradle 指令碼,請先在電腦上配置好 gradle 的環境變數,這樣就可以方便地執行 gradle 指令碼了。

二. Groovy 語言基礎

由於篇幅所限,本篇文章也只能作為一個引子,介紹基礎的 Groovy 語言概念,詳細的還需要從 Groovy 語言文件 學習。而且我個人認為,如果遇到什麼不懂的、不會的,從官方文件上學習是最好的學習途徑;或者至少先從官方文件上學習,再去學習其他的資料,將自己學習的和資料的進行對比思考,這樣會更有助於個人的成長

為了避免無意義的內容,只介紹和 Java 有區別的地方,相同的地方不作說明。

2.1 變數

  1. Groovy 中宣告的變數,預設的修飾符是 public 的
  2. Groovy 中宣告變數時,如果一行只宣告一個變數則可以省略末尾的 ;,但是如果一行宣告瞭多個變數,變數與變數之間則不可以省略 ;
  3. 在 Groovy 中宣告變數,也可以使用關鍵字 defdef 只是宣告瞭一個變數,變數的實際型別根據該變數的物件決定。def 和 JavaScript 中的 val 有點像,從 def 可以看出 Groovy 也是一門動態語言
  4. Groovy 中字串 String,可以使用單引號 'String',也可以使用雙引號 "String"
  5. 在 Groovy 中的 String,可以通過 ${} 做佔位符表示式向字串中插入值,在 {} 中寫表示式或變數都可以,使用 ${} 的字串必須使用雙引號 ""
int version = 1
Boolean isDebug = true
def language = 'groovy'
def message = "Hello from ${language + 1}, the result is ${isDebug}."

task hello {
	doLast{
		println message	
	}
}
複製程式碼

上面程式碼的執行輸出是:

> Task :hello
Hello from groovy1, the result is true.
複製程式碼

2.2 List

  1. 因為在 Groovy 中沒有定義任何集合類,所以 Groovy 中的 List 使用的是 JDK 中的 java.util.List
  2. 在 Groovy 中的一個 List 中可以新增多種型別的物件元素
  3. 建立 List 物件使用 [],而不是 Java 中的 {},防止和 Groovy 中的 閉包 Closure {} 混淆
  4. 可以通過 [index] 的方式修改和訪問 List 中的元素
  5. 可以通過 << 向 List 中新增元素,<< 實際是 leftShift() 方法
  6. 可以通過負數,從後向前訪問 List 中的元素,比如 [-1] 表示最後一個元素
  7. 可以通過 [index1, index2] 同時訪問 List 中的多個元素,返回結果仍是一個List
  8. 可以通過 [index1..index2] 一次性訪問 List 中某個範圍內的陣列,返回結果也是一個 List
ArrayList arrayList = ['arrayOne', 'arrayTwo', 'arrayThree']
LinkedList linkedList = ['linkedOne', 'linkedTwo', 'linkedThree']
List list = [1, 2, true]
def listDef = ['one', 2, true, 4, '5']

task helloList {
    doLast {
        println listDef
        println arrayList
        println linkedList
        println list
        println list[0]
        println list[-1]
        list << 4
        println list[-1]
        println list[1, 3]
        println list[1..3]
    }
}
複製程式碼

輸出如下所示:

> Task :app:helloList
[one, 2, true, 4, 5]
[arrayOne, arrayTwo, arrayThree]
[linkedOne, linkedTwo, linkedThree]
[1, 2, true]
1
true
4
[2, 4]
[2, true, 4]

複製程式碼

2.3 Arrays

Groovy 中的陣列和 Java 中的陣列區別並不大,也不過多的做介紹

  1. Groovy 中的陣列使用 [] 初始化,並不使用 {},防止和 Groovy 中的 閉包 Closure {} 混淆
  2. 陣列不支援 << 向 Arrays 中新增元素
String[] arrayStrings = ["one", "two", 'three'];
def arrayInts = [1, 2, 3] as int[]

task hello {

	doLast {
		println arrayStrings[0]
		println arrayStrings[1]
		println arrayStrings[-1]
		// arrayStrings << 'four'	// Arrays 不支援 << 
		println arrayStrings
		println arrayInts
	}
}
複製程式碼

輸出如下所示:

> Task :hello
one
two
three
[one, two, three]
[1, 2, 3]
複製程式碼

2.4 Map

  1. Groovy 中的 Map 是以 : 作為 key 和 value 的連線,並且以 , 做為每一項的分隔符的
  2. Map 中的 key 既可以是字串也可以是阿拉伯數字
  3. 可以通過 [key].key 的形式訪問或向 map 中賦值,訪問的時候如果不存在該 key,則會返回 null
  4. 如果以變數作為 key 訪問 map 時,記得需要加上 ()
def maps = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']
def mapNums = [1: 'one', 2: 'two', 3: 'three', 100: 'four']

def key = 'name'
def mapKey = [key: 'value']
def mapKey1 = [(key): 'value1']

task helloMaps {

    doLast {

        println maps['red']
        println maps.green
        maps['pink'] = '#FF00FF'
        maps.yello = '#FFFF00'
        println maps['pink']
        println maps.yello
        println maps.white

        println mapNums[1]
        println mapNums[100]
        println mapNums[5]

        println mapKey['key']
        println mapKey['name']

        println mapKey1['name']
    }
}
複製程式碼

上述程式碼的輸出是

> Task :app:helloMaps
#FF0000
#00FF00
#FF00FF
#FFFF00
null
one
four
null
value
null
value1
複製程式碼

2.5 Class 和物件

Groovy 中的 Class 和 Java 中的 Class 區別並不大,主要有以下幾個區別

  1. 如果類、方法沒有修飾符的話,預設是 public 修飾符的
  2. 如果類中的變數 fields 沒有被修飾符修飾的話,會自動成為一個 propertiesproperties 是公有的,並且會自動生成該 properties 的 setter 和 getter 方法
  3. 在 Java 中,檔名和主類的名稱必須一致,但是 Groovy 中並沒有這個限制,且在一個 Groovy 檔案中可以出現多個 public 的類
  4. 在一個 Groovy 檔案中可以在類之外定義方法或語句,這種檔案就是指令碼了
class Student {
    def name
    def age
    private String work
    public String lover

    def Student(String name) {
        this.name = name
    }
}

task helloClass {

    doLast {
        def student = new Student('lijiankun24')
        println student.name
        println student.getAge()
        println student.lover
        // println student.getLover()       // Student 中並沒有 getLover() 這個方法
        // println student.getWork()        // Student 中並沒有 getWork() 這個方法
    }
}
複製程式碼

輸出結果如下:

> Task :app:helloClass
lijiankun24
null
null
複製程式碼

2.6 函式

Groovy 中的函式和 Java 中的函式並沒有太大的區別

  1. 函式一定會有返回值,如果沒有顯示的使用 return 返回值的話,函式的最後一行語句的執行結果作為值返回,可能返回值是個 null
  2. 如果函式有引數,呼叫函式的時候,可以省略函式的括號,函式名和引數之間需要用空格間隔;如果函式沒有引數,呼叫函式的時候就不能省略括號
  3. 函式內不可以訪問函式外的變數
def message = 'message'

def printMessage () {
    println message
}

void printName(String name) {
    println name
}

void printPerson(String name, age) {
    println "The name is ${name} and the age is ${age}"
}

task helloFunction {
    doLast {
        println printName('xiaoming')

        printPerson  'xiaoming', 20

        // println printMessage() 會執行異常
    }
}
複製程式碼

輸出結果如下所示:

> Task :app:helloFunction
xiaoming
null
The name is xiaoming and the age is 20
複製程式碼

2.7 閉包 Closure

閉包 closure 是 Java 中沒有的,也是需要重點學習的,學好 closure 對理解 Android 中的 Gradle 會有莫大的幫助

  1. 閉包 closure 的定義如下,其中 [closureParameters ->] 作為引數部分,是可以省略的
{ [closureParameters -> ] statements }
複製程式碼
  1. closure 其實是 Groovy 中 groovy.lang.Closure 的一個類
  2. 閉包 closure 可以訪問閉包之外的變數
  3. 閉包 closure 可以有三種呼叫方式,如下程式碼所示
  4. 閉包 closure 的引數可以省略,預設是有個 it 引數的
  5. 閉包 closure 也可以作為另一個閉包 closure 的引數
// 閉包可以訪問閉包之外的變數
def message = 'closure is good'
def printMessage = {
    println "The message is '${message}'"
}

// 閉包實際上是一個 `groovy.lang.Closure` 類
Closure<Boolean> booleanClosure = {
    return it == 'xiaoming'
}

// 閉包可以省略引數,預設有一個 `it` 的引數
def testClosure = {
    println "I am a closure, the params is ${it}."
}

// 閉包可以有多個引數,引數可以指定型別,也可以不指定型別
def testClosureParams = { name, int age ->
    println "I am a closure, the params is ${name}."
}

// 閉包可以作為另一個閉包的引數
def paramsClosure = { name, closure ->
    if (closure(name)) {
        println 'The input name is xiaoming'
    } else {
        println 'The input name is not xiaoming'
    }
}

task helloClosure {
    doLast {
        printMessage()
        println booleanClosure('xiaoming')
        println booleanClosure('test')

        // 閉包的三種呼叫方式
        testClosure 'xiaoming'
        testClosure.call('xiaoming')
        testClosure('xiaoming')

        testClosureParams('xiaoming', 20)
        
        // 閉包 booleanClosure 作為閉包 paramsClosure 的引數
        paramsClosure('xiaoming', booleanClosure)
        paramsClosure('test', booleanClosure)

        // 可以在呼叫閉包的時候才去定義引數閉包的定義,使用非常方便簡潔
        paramsClosure('xiaoming', { name ->
            name.toUpperCase() == 'XIAOMING'
        })
    }
}
複製程式碼

輸出如下所示

> Task :app:helloClosure
The message is 'closure is good'
true
false
I am a closure, the params is xiaoming.
I am a closure, the params is xiaoming.
I am a closure, the params is xiaoming.
I am a closure, the params is xiaoming.
The input name is xiaoming
The input name is not xiaoming
The input name is xiaoming
複製程式碼

Groovy 的基礎知識就是這麼多,如果想學習更多的內容,建議學習 Groovy 文件

相關文章