Gradle Groovy 基礎語法 MD

Markdown版本筆記 個人GitHub首頁 個人博客 個人微信 個人郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

Gradle Groovy 基礎語法 MDhtml


目錄

Groovy 基礎

docs文檔
api文檔
參考1
參考2java

爲什麼要學 Groovy

Gradle是目前Android主流的構建工具,無論你是經過命令行仍是經過AndroidStudio來build,最終都是經過Gradle來實現的。因此學習Gradle很是重要。git

目前國內對Android領域的探索已經愈來愈深,很多技術領域如插件化、熱修復、構建系統等都對Gradle有迫切的需求,不懂Gradle將沒法完成上述事情。因此Gradle必需要學習。github

Gradle不僅僅是一個配置腳本,它的背後是幾門語言:api

  • Groovy Language
  • Gradle DSL
  • Android DSL

DSL的全稱是Domain Specific Language,即領域特定語言,或者直接翻譯成「特定領域的語言」,再直接點,其實就是這個語言不通用,只能用於特定的某個領域,俗稱「小語言」。所以DSL也是語言。微信

實際上,Gradle腳本大多都是使用groovy語言編寫的。閉包

Groovy是一門jvm語言,功能比較強大,細節也不少,所有學習的話比較耗時,對咱們來講收益較小,而且玩轉Gradle並不須要學習Groovy的所有細節,因此其實咱們只須要學一些Groovy基礎語法與API便可。app

爲什麼要使用 Groovy

Groovy是一種基於JVM的敏捷開發語言,它結合了衆多腳本語言的強大的特性,因爲同時又能與Java代碼很好的結合。一句話:既有面向對象的特性又有純粹的腳本語言的特性。jvm

因爲Groovy運行在JVM上,所以也可使用Java語言編寫的組建。ide

簡單來講,Groovy提供了更加靈活簡單的語法大量的語法糖以及閉包特性可讓你用更少的代碼來實現和Java一樣的功能

如何編譯運行 Groovy

Groovy是一門jvm語言,它最終是要編譯成class文件而後在jvm上執行,因此Java語言的特性Groovy都支持,Groovy支持99%的java語法,咱們徹底能夠在Groovy代碼中直接粘貼java代碼。

能夠安裝Groovy sdk來編譯和運行。可是我並不想搞那麼麻煩,畢竟咱們的最終目的只是學習Gradle。

推薦你們經過這種方式來編譯和運行Groovy。

在當面目錄下建立build.gradle文件,在裏面建立一個task,而後在task中編寫Groovy代碼便可,以下所示:

task(testGroovy).doLast {
   println "開始運行自定義task"
   test()
}

def test() {
   println "執行Groovy語法的代碼"
   System.out.println("執行Java語法的代碼!");
}

而後在命令行終端中執行以下命令便可:

gradle testGroovy
> Configure project :app 

> Task :app:testGroovy 
開始運行自定義task
執行Groovy語法的代碼
執行Java語法的代碼!

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

咱們知道,在Android項目中,咱們只要更改build.gradle文件一點內容,AS就會提示咱們同步:

可是在咱們測試 Groovy 時中,咱們更改build.gradle文件後能夠沒必要去同步,執行命令時會自動執行你修改後的最新邏輯。

最基本的語法

Groovy中的類和方法默認都是public權限的,因此咱們能夠省略public關鍵字,除非咱們想使用private。
Groovy中的類型是弱化的,全部的類型均可以動態推斷,可是Groovy仍然是強類型的語言,類型不匹配仍然會報錯。
Groovy中經過 def 關鍵字來聲明變量和方法
Groovy中不少東西都是能夠省略的,好比

  • 語句後面的分號是能夠省略的
  • def 和 變量的類型 中的其中之一是能夠省略的(不能所有省略)
  • def 和 方法的返回值類型 中的其中之一是能夠省略的(不能所有省略)
  • 方法調用時的圓括號是能夠省略的
  • 方法聲明中的參數類型是能夠省略的
  • 方法的最後一句表達式可做爲返回值返回,而不須要return關鍵字

    省略return關鍵字並非一個好的習慣,就如同 if else while 後面只有一行語句時能夠省略大括號同樣,之後若是添加了其餘語句,頗有可能會致使邏輯錯誤

def int a = 1; //若是 def 和 類型同時存在,IDE 會提示你"def是不須要的(is unnecessary)"
def String b = "hello world" //省略分號,存在分號時也會提示你 unnecessary
def c = 1 //省略類型

def hello() {  //省略方法聲明中的返回值類型
   println ("hello world");
   println "hello groovy" //省略方法調用時的圓括號
   return 1;
}

def hello(String msg) {
   println "hello" + msg //省略方法調用時的圓括號
   1;                    //省略return
}

int hello(msg) { //省略方法聲明中的參數類型
   println msg
   return 1 // 這個return不能省略
   println "done" //這一行代碼是執行不到的,IDE 會提示你 Unreachable statement,但語法沒錯
}

支持的數據類型

在Groovy中,數據類型有:

  • Java中的基本數據類型
  • Java中的對象
  • Closure(閉包)
  • 增強的List、Map等集合類型
  • 增強的File、Stream等IO類型

類型能夠顯示聲明,也能夠用 def 來聲明,用 def 聲明的類型Groovy將會進行類型推斷。
基本數據類型和對象和Java中的一致,只不過在Gradle中,對象默認的修飾符爲public

String

String的特點在於字符串的拼接,好比

def a = 1
def b = "hello"
def c = "a=${a}, b=${b}"
println c //a=1, b=hello

閉包

Groovy中有一種特殊的類型,叫作Closure,翻譯過來就是閉包,這是一種相似於C語言中函數指針的東西。
閉包用起來很是方便,在Groovy中,閉包做爲一種特殊的數據類型而存在,閉包能夠做爲方法的參數和返回值,也能夠做爲一個變量而存在
閉包能夠有返回值和參數,固然也能夠沒有。下面是幾個具體的例子:

def test() {
    def closure = { String parameters -> //閉包的基本格式
        println parameters
    }
    def closure2 = { a, b -> // 省略了閉包的參數類型
        println "a=${a}, b=${b}"
    }
    def closure3 = { a ->
        a + 1 //省略了return
    }
    def closure4 = { // 省略了閉包的參數聲明
        println "參數爲 ${it}" //若是閉包不指定參數,那麼它會有一個隱含的參數 it
    }

    closure("包青天") //包青天
    closure2 10086, "包青天" //a=10086, b=包青天
    println closure3(1) //2
    //println closure3 2 //不容許省略圓括號,會提示:Cannot get property '1' on null object
    closure4() //參數爲 null
    closure4 //不容許省略圓括號,可是並不會報錯
    closure4 10086 //參數爲 10086
}

閉包的一個難題是如何肯定閉包的參數(包括參數的個數、參數的類型、參數的意義),尤爲當咱們調用Groovy的API時,這個時候沒有其餘辦法,只有查詢Groovy的文檔才能知道。

List和Map

Groovy增強了Java中的集合類,好比List、Map、Set等。

基本使用以下:

def emptyList = []
def list = [10086, "hello", true]
list[1] = "world"
assert list[1] == "world"
println list[0] //10086
list << 5 //至關於 add()
assert 5 in list // 調用包含方法
println list //[10086, world, true, 5]
def range = 1..5
assert 2 in range
println range //1..5
println range.size() //5
def emptyMap = [:]
def map = ["id": 1, "name": "包青天"]
map << [age: 29] //添加元素
map["id"] = 10086 //訪問元素方式一
map.name = "哈哈" //訪問元素方式二,這種方式最簡單
println map //{id=10086, name=哈哈, age=29}

能夠看到,經過Groovy來操做List和Map顯然比Java簡單的多。

上面有一個看起來很奇怪的操做符<<,其實這並無什麼大不了,<<表示向List中添加新元素的意思,這一點從 List文檔 當也能查到。

public List leftShift(Object value)
  • Overloads the left shift operator to provide an easy way to append objects to a List. 重載左移位運算符,以提供將對象append到List的簡單方法。
  • Parameters: value - an Object to be added to the List.
  • Returns: same List, after the value was added to it.

實際上,這個運算符是大量使用的,而且當你用 leftShift 方法時 IDE 也會提示你讓你使用左移位運算符<<替換:

def list = [1, 2]
list.leftShift 3
assert list == [1, 2, 3]
list << 4
println list //[1, 2, 3, 4]

閉包的參數

這裏藉助Map再講述下如何肯定閉包的參數。好比咱們想遍歷一個Map,咱們想採用Groovy的方式,經過查看文檔,發現它有以下兩個方法,看起來和遍歷有關:

  • Map each(Closure closure):Allows a Map to be iterated through using a closure.
  • Map eachWithIndex(Closure closure):Allows a Map to be iterated through using a closure.

能夠發現,這兩個each方法的參數都是一個閉包,那麼咱們如何知道閉包的參數呢?固然不能靠猜,仍是要查文檔。

public Map each(Closure closure)
  • 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.
  • In general, the order in which the map contents are processed cannot be guaranteed(一般沒法保證處理元素的順序). In practise(在實踐中), specialized forms of Map(特殊形式的Map), e.g. a TreeMap will have its contents processed according to the natural ordering(天然順序) of the map.
def result = ""
[a:1, b:3].each { key, value -> result += "$key$value" } //兩個參數
assert result == "a1b3"
def result = ""
[a:1, b:3].each { entry -> result += entry } //一個參數
assert result == "a=1b=3"

[a: 1, b: 3].each { println "[${it.key} : ${it.value}]" } //一個隱含的參數 it,key 和 value 是屬性名

試想一下,若是你不知道查文檔,你又怎麼知道each方法如何使用呢?光靠從網上搜,API文檔中那麼多接口,搜的過來嗎?記得住嗎?

增強的IO

在Groovy中,文件訪問要比Java簡單的多,不論是普通文件仍是xml文件。怎麼使用呢?查來 File文檔

public Object eachLine(Closure closure)
  • Iterates through this file line by line. Each line is passed to the given 1 or 2 arg closure. The file is read using a reader which is closed before this method returns.
  • Parameters: closure - a closure (arg 1 is line, optional arg 2 is line number starting at line 1)
  • Returns: the last value returned by the closure

能夠看到,eachLine方法也是支持1個或2個參數的,這兩個參數分別是什麼意思,就須要咱們學會讀文檔了,一味地從網上搜例子,多累啊,並且很難完全掌握:

def file = new File("a.txt")
file.eachLine { line, lineNo ->
    println "${lineNo} ${line}" //行號,內容
}

file.eachLine { line ->
    println "${line}" //內容
}

除了eachLine,File還提供了不少Java所沒有的方法,你們須要瀏覽下大概有哪些方法,而後須要用的時候再去查就好了,這就是學習Groovy的正道。

訪問xml文件

Groovy訪問xml有兩個類:XmlParserXmlSlurper,兩者幾乎同樣,在性能上有細微的差異,不過這對於本文不重要。
groovy.util.XmlParserAPI文檔

文檔中的案例:

def xml = '<root><one a1="uno!"/><two>Some text!</two></root>'
//或者 def xml = new XmlParser().parse(new File("filePath.xml"))
def rootNode = new XmlParser().parseText(xml) //根節點
assert rootNode.name() == 'root' //根節點的名稱
assert rootNode.one[0].@a1 == 'uno!' //根節點中的子節點 one 的 a1 屬性的值
assert rootNode.two.text() == 'Some text!'  //根節點中的子節點 two 的內容
rootNode.children().each { assert it.name() in ['one','two'] }

更多的細節查文檔便可。

其餘的一些語法特性

Getter和Setter

當你在Groovy中建立一個beans的時候,一般咱們稱爲POGOS(Plain Old Groovy Objects),Groovy會自動幫咱們建立getter/setter方法。
當你對getter/setter方法有特殊要求,你儘可提供本身的方法,Groovy默認的getter/setter方法會被替換。

構造器

有一個bean

class Server {
    String name
    Cluster cluster
}

初始化一個實例的時候你可能會這樣寫:

def server = new Server()
server.name = "Obelix"
server.cluster = aCluster

其實你能夠用帶命名的參數的默認構造器,會大大減小代碼量:

def server = new Server(name: "Obelix", cluster: aCluster)

Class類型

在Groovy中Class類型的.class後綴不是必須的,好比:

def func(Class clazz) {
    println clazz
}
func(File.class) //class java.io.File
func(File) //class java.io.File

使用with()操做符

當更新一個實例的時候,你可使用with()來省略相同的前綴,好比:

Book book = new Book() 
book.with {
   id = 1 //等價於 book.id = 1
   name = "包青天"
   start(10086)
   stop("包青天")
}

判斷是否爲真

全部類型都能轉成布爾值,好比nullvoid至關於0或者至關於false,其餘則至關於true,因此:

if (name) {}
//等價於
if (name != null && name.length > 0) {}

在Groovy中能夠在類中添加asBoolean()方法來自定義是否爲真

簡潔的三元表達式

在Groovy中,三元表達式能夠更加簡潔,好比:

def result = name ?: ""
//等價於
def result = name != null ? name : ""

捕獲任何異常

若是你實在不想關心try塊裏拋出何種異常,你能夠簡單的捕獲全部異常,而且能夠省略異常類型:

try {
    // ...
} catch (any) { //能夠省略異常類型
    // something bad happens
}

這裏的any並不包括Throwable,若是你真想捕獲everything,你必須明確的標明你想捕獲Throwable

簡潔的非空判斷

在java中,你要獲取某個對象的值必需要檢查是否爲null,這就形成了大量的if語句;在Groovy中,非空判斷能夠用?.表達式,好比:

println order?.customer?.address
//等價於
if (order != null) {
   if (order.getCustomer() != null) {
       if (order.getCustomer().getAddress() != null) {
           System.out.println(order.getCustomer().getAddress());
       }
   }
}

使用斷言

在Groovy中,可使用assert來設置斷言,當斷言的條件爲false時,程序將會拋出異常

def check(String name) {
   assert name // 檢查方法傳入的參數是否爲空,name non-null and non-empty according to Groovy Truth
   assert name?.size() > 3
}

==和equals

Groovy裏的is()方法等同於Java裏的==
Groovy中的==是更智能的equals(),比較兩個類的時候,你應該使用a.is(b)而不是==
Groovy中的==能夠自動避免NullPointerException異常

status == "包青天"
//等價於Java中的
status != null && status.equals("包青天")

switch方法

在Groovy中,switch方法變得更加靈活,能夠同時支持更多的參數類型:

def x = null
def result = ""
switch (x) {
    case "foo": result = "found foo" //沒有 break 時會繼續向下判斷
    case "bar": result += "bar"
        break
    case [4, 5, 6]: result = "list" //匹配集合中的元素
        break
    case 12..30: result = "range" //匹配某個範圍內的元素
        break
    case Integer: result = "integer" //匹配Integer類型
        break
    case { it > 3 }: result = "number > 3" //匹配表達式
        break
    case Number: result = "number" //匹配Number類型
        break
    default: result = "default"
}
println result

字符串分行

Java中,字符串過長鬚要換行時咱們通常會這樣寫:

throw new PluginException("Failed to execute command list-applications:" +
    " The group with name " +
    parameterMap.groupname[0] +
    " is not compatible group of type " +
    SERVER_TYPE_NAME)

Groovy中你能夠用 \ 字符,而不須要添加一堆的雙引號:

throw new PluginException("Failed to execute command list-applications: \
The group with name ${parameterMap.groupname[0]} \
is not compatible group of type ${SERVER_TYPE_NAME}")

或者使用多行字符串""":

throw new PluginException("""Failed to execute command list-applications:
    The group with name ${parameterMap.groupname[0]}
    is not compatible group of type ${SERVER_TYPE_NAME)}""")

Groovy中,單引號引發來的字符串是java字符串,不能使用佔位符來替換變量,雙引號引發的字符串則是java字符串或者Groovy字符串。

Import 別名

在java中使用兩個類名相同但包名不一樣的兩個類,像java.util.Listjava.wt.List,你必須使用完整的包名才能區分。Groovy中則可使用import別名:

import java.util.List as jurist //使用別名
import java.awt.List as aList
import java.awt.WindowConstants as WC
import static pkg.SomeClass.foo //靜態引入方法

2019-1-12

相關文章
相關標籤/搜索