前言
成為一名優秀的Android開發,需要一份完備的 知識體系,在這裡,讓我們一起成長為自己所想的那樣~。
Groovy 作為 Gradle 這一強大建構工具的核心語言,其重要性不言而喻,但是 Groovy 本身是十分複雜的,要想全面地掌握它,我想幾十篇萬字長文也無法将其徹底描述。所幸的是,在 Gradle 領域中涉及的 Groovy 知識都是非常基礎的,是以,本篇文章的目的是為了在後續深入探索 Gradle 時做好一定的基礎儲備。
一、DSL 初識
DSL(domain specific language),即領域特定語言,例如:Matliba、UML、HTML、XML 等等 DSL 語言。可以這樣了解,Groovy 就是 DSL 的一個分支。
特點
- 1)、解決特定領域的專有問題。
- 2)、它與系統程式設計語言走的是兩個極端,系統程式設計語言是希望解決所有的問題,比如 Java 語言希望能做 Android 開發,又希望能做伺服器開發,它具有橫向擴充的特性。而 DSL 具有縱向深入解決特定領域專有問題的特性。
總的來說,DSL 的 核心思想 就是:“求專不求全,解決特定領域的問題”。
二、Groovy 初識
1、Groovy 的特點
Groovy 的特點具有如下 三點:
- 1)、Groovy 是一種基于 JVM 的靈活開發語言。
- 2)、Groovy 結合了 Python、Ruby 和 Smalltalk 衆多腳本語言的許多強大的特性。
- 3)、Groovy 可以與 Java 完美結合,而且可以使用 Java 所有的庫。
那麼,在已經有了其它腳本語言的前提下,為什麼還要制造出 Grvooy 語言呢?
因為 Groovy 語言相較其它程式設計語言而言,其 入門的學習成本是非常低的,因為它的文法就是對 Java 的擴充,是以,我們可以用學習 Java 的方式去學習 Groovy。
2、Groovy 語言本身的特性
其特性主要有如下 三種:
- 1)、文法上支援動态類型,閉包等新一代語言特性。并且,Groovy 語言的閉包比其它所有語言類型的閉包都要強大。
- 2)、它可以無縫內建所有已經存在的 Java 類庫,因為它是基于 JVM 的。
- 3)、它即可以支援面向對象程式設計(基于 Java 的擴充),也可以支援面向過程程式設計(基于衆多腳本語言的結合)。
需要注意的是,在我們使用 Groovy 進行 Gradle 腳本編寫的時候,都是使用的面向過程進行程式設計的。
3、Groovy 的優勢
Groovy 的優勢有如下 四種:
- 1)、它是一種更加靈活的程式設計語言:在文法上建構除了非常多的文法糖,許多在 Java 層需要寫的代碼,在 Groovy 中是可以省略的。是以,我們可以用更少的代碼實作更多的功能。
- 2)、入門簡單,但功能非常強大。
- 3)、既可以作為程式設計語言也可以作為腳本語言
- 4)、熟悉掌握 Java 的同學會非常容易掌握 Groovy。
4、Groovy 包的結構
Groovy 官方網址
從官網下載下傳好 Groovy 檔案之後,我們就可以看到 Groovy 的目錄結構,其中我們需要 重點關注 bin 和 doc 這個兩個檔案夾。
bin 檔案夾
bin 檔案夾的内容如下所示:
這裡我們了解下三個重要的可執行指令檔案,如下所示:
- 1)、groovy 指令類似于 Java 中的 java 指令,用于執行 groovy Class 位元組碼檔案。
- 2)、groovyc 指令類似于 Java 中的 javac 指令,用于将 groovy 源檔案編譯成 groovy 位元組碼檔案。
- 3)、groovysh 指令是用來解釋執行 groovy 腳本檔案的。
doc 檔案夾
在 doc 檔案夾的下面有一個 html 檔案,其中的内容如下所示:
這裡的 api 和 documentation 是我們需要重點關注的,其作用分别如下所示:
- api:groovy 中為我們提供的一系列 API 及其 說明文檔。
- documentation:groovy 官方為我們提供的一些教程。
5、Groovy 中的關鍵字
下面是 Groovy 中所有的關鍵字,命名時尤其需要注意,如下所示:
6、Groovy && Java 差異學習
1)、getter / setter
對于每一個 field,Groovy 都會⾃動建立其與之對應的 getter 與 setter 方法,從外部可以直接調用它,并且 在使⽤ object.fieldA 來擷取值或者使用 object.fieldA = value 來指派的時候,實際上會自動轉而調⽤ object.getFieldA() 和 object.setFieldA(value) 方法。
如果我們不想調用這個特殊的 getter 方法時則可以使用 .@ 直接域通路操作符。
2)、除了每行代碼不用加分号外,Groovy 中函數調用的時候還可以不加括号。
需要注意的是,我們在使用的時候,如果目前這個函數是 Groovy API 或者 Gradle API 中比較常用的,比如 println,就可以不帶括号。否則還是帶括号。不然,Groovy 可能會把屬性和函數調用混淆。
3)、Groovy 語句可以不用分号結尾。
4)、函數定義時,參數的類型也可以不指定。
5)、Groovy 中函數的傳回值也可以是無類型的,并且無傳回類型的函數,其内部都是按傳回 Object 類型來處理的。
6)、目前函數如果沒有使用 return 關鍵字傳回值,則會預設傳回 null,但此時必須使用 def 關鍵字。
7)、在 Groovy 中,所有的 Class 類型,都可以省略 .class。
8)、在 Groovy 中,== 相當于 Java 的 equals,,如果需要比較兩個對象是否是同一個,需要使用 .is()。
9)、Groovy 非運算符如下:
10)、Groovy 支援 ** 次方運算符,代碼如下所示:
11)、判斷是否為真可以更簡潔:
12)、三元表達式可以更加簡潔:
13)、簡潔的非空判斷
14)、使用 assert 來設定斷言,當斷言的條件為 false 時,程式将會抛出異常。
15)、可以使用 Number 類去替代 float、double 等類型,省去考慮精度的麻煩。
16)、switch 方法可以同時支援更多的參數類型。
注意,swctch 可以比對清單當中任一進制素,示例代碼如下所示:
三、Groovy 基礎文法
Groovy 的基礎文法主要可以分為以下 四個部分:
- 1)、Groovy 核心基礎文法。
- 2)、Groovy 閉包。
- 3)、Groovy 資料結構。
- 4)、Groovy 面向對象
1、Groovy 核心基礎文法
Groovy 中的變量
變量類型
Groovy 中的類型同 Java 一樣,也是分為如下 兩種:
- 1)、基本類型。
- 2)、對象類型。
但是,其實 Groovy 中并沒有基本類型,Groovy 作為動态語言, 在它的世界中,所有事物都是對象,就如 Python、Kotlin 一樣:所有的基本類型都是屬于對象類型。為了驗證這個 Case,我們可以建立一個 groovy 檔案,建立一個 int 類型的變量并輸出它,結果如下圖所示:
可以看到,上面的輸出結果為 'class java.lang.Integer',是以可以驗證我們的想法是正确的。實際上,Groovy 的編譯器會将所有的基本類型都包裝成對象類型。
變量定義
groovy 變量的定義與 Java 中的方式有比較大的差異,對于 groovy 來說,它有 兩種定義方式,如下所示:
- 1)、強類型定義方式:groovy 像 Java 一樣,可以進行強類型的定義,比如上面直接定義的 int 類型的 x,這種方式就稱為強類型定義方式,即在聲明變量的時候定義它的類型。
- 2)、弱類型定義方式:不需要像強類型定義方式一樣需要提前指定類型,而是通過 def 關鍵字來定義我們任何的變量,因為編譯器會根據值的類型來為它進行自動的指派。
下面,我們就使用 def 關鍵字來定義一系列的變量,并輸出它們的類型,來看看是否編譯器會識别出對應的類型,其結果如下圖所示:
可以看到,編譯器的确會自動自動推斷對應的類型。
那麼,這兩種方式應該分别在什麼樣的場景中使用呢?
如果這個變量就是用于目前類或檔案,而不會用于其它類或應用子產品,那麼,建議使用 def 類型,因為在這種場景下弱類型就足夠了。
但是,如果你這個類或變量要用于其它子產品的,建議不要使用 def,還是應該使用 Java 中的那種強類型定義方式,因為使用強類型的定義方式,它不能動态轉換為其它類型,它能夠保證外界傳遞進來的值一定是正确的。如果你這個變量要被外界使用,而你卻使用了 def 類型來定義它,那外界需要傳遞給你什麼才是正确的呢?這樣會使調用方很疑惑。
如果此時我們在後面的代碼中改變上圖中 x1 的值為 String 類型,那麼 x1 又會被編譯器推斷為 String 類型,如下圖所示:
于是我們可以猜測到,其實使用 def 關鍵字定義出來的變量就是 Obejct 類型。
Groovy 中的字元串
Groovy 中的字元串與 Java 中的字元串有比較大的不同,是以這裡我們需要着重了解一下。
Groovy 中的字元串除了繼承了 Java 中傳統 String 的使用方式之前,還 新增 了一個 GString 類型,它的使用方式至少有七、八種,但是常用的有三種定義方式。此外,在 GString 中新增了一系列的操作符,這能夠讓我們對 String 類型的變量有 更便捷的操作。最後,在 GString 中還 新增 了一系列好用的 API,我們也需要着重學習一下。
Groovy 中常用的三種字元串定義方式
在 Groovy 中有 三種常用 的字元串定義方式,如下所示:
- 1)、單引号 '' 定義的字元串
- 2)、雙引号 "" 定義的字元串
- 3)、三引号 '""' 定義的字元串
首先,需要說明的是,'不管是單引号、雙引号還是三引号,它們的類型都是 java.lang.String'。
那麼,單引号與三引号的差別是什麼呢?
既生瑜何生亮,其實不然。當我們編寫的單引号字元串中有轉義字元的時候,需要添加 '',并且,當字元串需要具備多行格式的時候,強行将單引号字元串分成多行格式會變成由 '+' 号組成的字元串拼接格式。
那麼,雙引号定義的變量又與單引号、三引号有什麼差別呢?
雙引号不同與單、三引号,它定義的是一個可擴充的變量。這裡我們先看看兩種雙引号的使用方式,如下圖所示:
在上圖中,第一個定義的 author 字元串就是正常的 String 類型的字元串,而下面定義的 study 字元串就是可擴充的字元串,因為它裡面使用了 '${author}' 的方式引用了 author 變量的内容。而且,從其最後的類型輸出可以看到,可擴充的類型就是 'org.codehaus.groovy.runtime.GStringImpl' 類型的。
需要注意的是,可擴充的字元串是可以擴充成為任意的表達式,例如數學運算,如下圖所示:
有了 Groovy 的這種可擴充的字元串,我們就可以 避免 Java 中字元串的拼接操作,提升 Java 程式運作時的性能。
那麼,既然有 String 和 GString 兩種類型的字元串,它們在互相指派的場景下需要不需要先強轉再指派呢?
這裡,我們可以寫一個 小栗子🌰 來看看實際的情況,如下圖所示:
可以看到,我們将 success 字元串傳入了 come 方法,但是最終得到的類型為 result,是以,可以說明 編譯器可以幫我們自動在 String 和 GString 之間互相轉換,我們在編寫的時候并不需要太過關注它們的差別。
2、Groovy 閉包(Closure)
閉包的本質其實就是一個代碼塊,閉包的核心内容可以歸結為如下三點:
- 1)、閉包概念
- 定義
- 閉包的調用
- 2)、閉包參數
- 普通參數
- 隐式參數
- 3)、閉包傳回值
- 總是有傳回值
閉包的調用
從 C/C++ 語言的角度看,閉包和函數指針很像,閉包可以通過 .call 方法來調用,也可以直接調用其構造函數,代碼如下所示:
如果閉包沒定義參數的話,則隐含有一個參數,這個參數名字叫 it,和 this 的作用類 似。it 代表閉包的參數。表示閉包中沒有參數的示例代碼:
注意點:省略圓括号
函數最後一個參數都是一個閉包,類似于回調函數的用法,代碼如下所示:
閉包的用法
閉包的常見用法有如下 四種:
- 1)、與基本類型的結合使用。
- 2)、與 String 類的結合使用。
- 3)、與資料結構的結合使用。
- 4)、與檔案等結合使用。
閉包進階
- 1)、閉包的關鍵變量
- this
- owner
- delegate
- 2)、閉包委托政策
閉包的關鍵變量
this 與 owner、delegate
其差異代碼如下代碼所示:
可以看到,如果我們直接在類、方法、變量中定義一個閉包,那麼這三種關鍵變量的值都是一樣的,但是,如果我們在閉包中又嵌套了一個閉包,那麼,this 與 owner、delegate 的值就不再一樣了。換言之,this 還會指向我們閉包定義處的類或者執行個體本身,而 owner、delegate 則會指向離它最近的那個閉包對象。
delegate 與 this、owner 的差異
其差異代碼如下代碼所示:
可以看到,delegate 的值是可以修改的,并且僅僅當我們修改 delegate 的值時,delegate 的值才會與 ownner 的值不一樣。
閉包的委托政策
其示例代碼如下所示:
需要注意的是,要想使上述 pretty 閉包的 delegate 修改生效,必須選擇其委托政策為 Closure.DELEGATE_ONLY,預設是 Closure.OWNER_FIRST 的。
3、Groovy 資料結構
Groovy 常用的資料結構有如下 四種:
- 1)、數組
- 2)、List
- 3)、Map
- 4)、Range
數組的使用和 Java 語言類似,最大的差別可能就是定義方式的擴充,如下代碼所示:
下面,我們看看其它三種資料結構。
1、List
即連結清單,其底層對應 Java 中的 List 接口,一般用 ArrayList 作為真正的實作類,List 變量由[]定義,其元素可以是任何對象。
連結清單中的元素可以通過索引存取,而且 不用擔心索引越界。如果索引超過目前連結清單長度,List 會自動往該索引添加元素。下面,我們看看 Map 最常使用的幾個操作。
1)、排序
2)、添加
3)、删除
4)、查找
5)、擷取最小值、最大值
6)、統計滿足條件的數量
Map
表示鍵-值表,其 底層對應 Java 中的 LinkedHashMap。
Map 變量由[:]定義,冒号左邊是 key,右邊是 Value。key 必須是字元串,value 可以是任何對象。另外,key 可以用 '' 或 "" 包起來,也可以不用引号包起來。下面,我們看看 Map 最常使用的幾個操作。
1)、存取
其示例代碼如下所示:
2)、each 方法
如果我們傳遞的閉包是一個參數,那麼它就把 entry 作為參數。如果我們傳遞的閉包是 2 個參數,那麼它就把 key 和 value 作為參數。
3)、eachWithIndex 方法
如果閉包采用兩個參數,則将傳遞 Map.Entry 和項目的索引(從零開始的計數器);否則,如果閉包采用三個參數,則将傳遞鍵,值和索引。
4)、groupBy 方法
按照閉包的條件進行分組,代碼如下所示:
5)、findAll 方法
它有兩個參數,findAll 會将 Key 和 Value 分别傳進 去。并且,如果 Closure 傳回 true,表示該元素是自己想要的,如果傳回 false 則表示該元素不是自己要找的。
Range
表示範圍,它其實是 List 的一種拓展。其由 begin 值 + 兩個點 + end 值表示。如果不想包含最後一個元素,則 begin 值 + 兩個點 + < + end 表示。我們可以通過 aRange.from 與 aRange.to 來獲對應的邊界元素。
如果需要了解更多的資料結構操作方法,我們可以直接查 Groovy API 詳細文檔 即可。
4、Groovy 面向對象
如果不聲明 public/private 等通路權限的話,Groovy 中類及其變量預設都是 public 的。
1)、元程式設計(Groovy 運作時)
Groovy 運作時的邏輯處理流程圖如下所示:
為了更好的講解元程式設計的用法,我們先建立一個 Person 類并調用它的 cry 方法,代碼如下所示:
為了實作元程式設計,我們需要使用 metaClass,具體的使用示例如下所示:
需要注意的是通過類的 metaClass 來添加元素的這種方式每次使用時都需要重新添加,幸運的是,我們可以在注入前調用全局生效的處理,代碼如下所示:
2)、腳本中的變量和作用域
對于每一個 Groovy 腳本來說,它都會生成一個 static void main 函數,main 函數中會調用一個 run 函數,腳本中的所有代碼則包含在 run 函數之中。我們可以通過如下的 groovyc 指令用于将編譯得到的 class 檔案拷貝到 classes 檔案夾下:
當我們在 Groovy 腳本中定義一個變量時,由于它實際上是在 run 函數中建立的,是以腳本中的其它方法或其他腳本是無法通路它的。這個時候,我們需要使用 @Field 将目前變量标記為成員變量,其示例代碼如下所示:
四、檔案處理
1、正常檔案處理
1)、讀檔案
eachLine 方法
我們可以使用 eachLine 方法讀該檔案中的每一行,它唯一的參數是一個 Closure,Closure 的參數是檔案每一行的内容。示例代碼如下所示:
然後,我們可以使用 'targetFile.bytes' 直接得到檔案的内容。
使用 InputStream
此外,我們也可以通過流的方式進行檔案操作,如下代碼所示:
使用閉包操作 inputStream
利用閉包來操作 inputStream,其功能更加強大,推薦使用這種寫法,如下所示:
2)、寫檔案
關于寫檔案有兩種常用的操作形式,即通過 withOutputStream/withInputStream 或 withReader/withWriter 的寫法。示例代碼如下所示:
通過 withOutputStream/、withInputStream copy 檔案
通過 withReader、withWriter copy 檔案
此外,我們也可以通過 withObjectOutputStream/withObjectInputStream 來儲存與讀取 Object 對象。示例代碼如下所示:
儲存對應的 Object 對象到檔案中
從檔案中讀取 Object 對象
2、XML 檔案操作
1)、擷取 XML 資料
首先,我們定義一個包含 XML 資料的字元串,如下所示:
然後,我們可以 使用 XmlSlurper 來解析此 xml 資料,代碼如下所示:
2)、擷取 XML 資料的兩種周遊方式
擷取 XML 資料有兩種周遊方式:深度周遊 XML 資料 與 廣度周遊 XML 資料,下面我們看看它們各自的用法,如下所示:
深度周遊 XML 資料
廣度周遊 XML 資料
在實際使用中,我們可以 利用 XmlSlurper 求擷取 AndroidManifest.xml 的版本号(versionName),代碼如下所示:
3)、生成 XML 資料
除了使用 XmlSlurper 解析 XML 資料之外,我們也可以 使用 xmlBuilder 來建立 XML 檔案,如下代碼所示:
4)、Groovy 中的 json
我們可以 使用 Groovy 中提供的 JsonSlurper 類去替代 Gson 解析網絡響應,這樣我們在寫插件的時候可以避免引入 Gson 庫,其示例代碼如下所示:
五、總結
在這篇文章中,我們從以下 四個方面 學習了 Groovy 中的必備核心文法:
- 1)、groovy 中的變量、字元串、循環等基本文法。
- 2)、groovy 中的資料結構:清單、映射、範圍。
- 3)、groovy 中的方法、類等面向對象、強大的運作時機制。
- 4)、groovy 中對普通檔案、XML、json 檔案的處理。
在後面我們自定義 Gradle 插件的時候需要使用到這些技巧,是以,掌握好 Groovy 的重要性不言而喻,隻有紮實基礎才能讓我們走的更遠。
公衆号
我的公衆号
JsonChao
開通啦,如果您想第一時間擷取最新文章和最新動态,歡迎掃描關注~
參考連結:
- 1、Groovy API 詳細文檔
- 2、《慕課網之Gradle3.0自動化項目建構技術精講+實戰》1 - 5章
- 3、《深入了解 Android 之 Gradle》
- 4、Gradle從入門到實戰 - Groovy基礎
- 5、Groovy腳本基礎全攻略
Contanct Me
● 微信:
歡迎關注我的微信: bcce5360
● 微信群:
由于微信群已超過 200 人,麻煩大家想進微信群的朋友們,加我微信拉你進群。
● QQ群:
2千人QQ群,Awesome-Android學習交流群,QQ群号:959936182, 歡迎大家加入~
About me
-
Email: [email protected]
-
Blog: jsonchao.github.io/
-
掘金: juejin.im/user/5a3ba9…