天天看點

Android的Gradle技巧 7. 深入groovy

7.  深入groovy

本附錄回顧了Groovy程式設計語言的基礎知識。 Gradle建構檔案主要由Groovy編寫的領域特定語言組成,用于建構。除了DSL之外,任何合法的Groovy代碼都可以添加到建構中。

Groovy是一種基于Java的通用程式設計語言,它編譯為Java位元組代碼。雖然它具有功能能力,但它是一種面向對象的語言,可以說是從C ++到Java的路徑中的下一代語言。

基本文法

Groovy的“Hello,World!”程式是示例A-1中所示的一個線程。

執行個體A-1。你好,世界!在Groovy

println'Hello,World!'

注意事項:

•分号是可選的。如果你添加它們,他們工作,但他們不是必需的。

•括号是可選的,直到它們不是。如果編譯器正确地猜測他們應該去哪裡,一切工作。否則,将它們添加回來。println方法接受一個String參數。這裡括号被省略。

•Groovy中有兩種類型的字元串:單引号字元串,如Hello,是java.lang.String的執行個體。雙引号字元串是Groovy字元串并允許插值,如示例A-2所示。

Groovy中沒有“原始”。所有變量都使用包裝器類,如java.lang.Integer,java.lang.Character和java.lang.Double。整數文本的本機資料類型(如3)為整數。浮點文字的本機資料類型,如3.5,是java.math.BigDecimal。

執行個體A-2: Groovy中的一些基本資料類型

assert 3.class == Integer
assert (3.5).class == BigDecimal
assert 'abc' instanceof String //1
assert "abc" instanceof String //2
String name = 'Dolly'
assert "Hello, ${name}!" == 'Hello, Dolly!'//3
assert "Hello, $name!" == 'Hello, Dolly!' //4
assert "Hello, $name!" instanceof GString
           

1單引号字元串是Java字元串

2雙引号字元串也是Java字元串,除非您插入

3字元串插值,完整形式

4字元串插值,短形式時沒有歧義

請注意,您可以調用文字上的方法,因為它們是包裝類的執行個體。

Groovy允許您使用實際類型(如String,Date或Employee)聲明變量,也可以使用def。參見實施例A-3。

執行個體A-3。靜态資料類型與動态資料類型

Integer n = 3
Date now = new Date()
def x = 3
assert x.class == Integer
x = 'abc'
assert x.class == String
x = new Date()
assert x.class == Date
           

Java自動導入java.lang包。在Groovy中,以下包都将自動導入:

•java.lang

•java.util

•java.io

•java.net

•groovy.lang

•groovy.util

類java.math.BigInteger和java.math.BigDecimal也可以沒有import語句。

斷言方法和Groovy真理

Groovy中的assert方法根據“Groovy真值”來評估其參數。這意味着:

•非零數(正和負)是真的

•非空集合,包括字元串,是真的

•非空引用為真

•布爾true為真

Groovy Truth在示例A-4中示出。

執行個體A-4。 Groovy真理

assert 3; assert -1; assert!0

assert  'abc'; assert!''; assert!“”

assert  [3,1,4,1,5,9]

assert ![]

傳遞的聲明什麼也不傳回。失敗的斷言抛出異常,如例A-5中,包含大量調試資訊。

實A-5。失敗的斷言

int x = 5; int y = 7

assert 12 == x + y //傳遞

assert 12 == 3 * x + 4.5 * y /(2 / x + y ** 3)//失敗

失敗斷言的結果如例A-6所示。

執行個體A-6。失敗斷言輸出

Exception thrown
Assertion failed:
assert 12 == 3 * x + 4.5 * y / (2/x + y**3)
| | | | | | | || | ||
false| 5 | | 7 | |5 | |343
15 | 31.5| 0.4| 7
| | 343.4
| 0.0917297612
15.0917297612
at ConsoleScript11.run(ConsoleScript11:4)
           

操作符重載

在Groovy中,每個運算符對應一個方法調用。例如,+号調用Number上的plus方法。這在Groovy庫中廣泛使用。一些執行個體示于實施例A-7中。

執行個體A-7。操作符重載

assert 3 + 4 == 3.plus(4)
assert 3 * 4 == 3.multiply(4)
assert 2**6 == 64
assert 2**6 == 2.power(6)
assert 'abc' * 3 == 'abcabcabc' // String.multiply(Number)
try {
3 * 'abc'
} catch (MissingMethodException e) {
// no Number.multiply(String) method
}
String s = 'this is a string'
assert s + ' and more' == 'this is a string and more'
assert s - 'is' == 'th is a string'
assert s - 'is' - 'is' == 'th a string'
Date now = new Date()
Date tomorrow = now + 1 // Date.plus(Integer)
assert tomorrow - 1 == now // Date.minus(Integer)
           

Groovy有一個求幂運算符**,如圖所示。

在Java中,==運算符檢查兩個引用是否配置設定給同一個對象。在Groovy中,==調用equals方法,是以它檢查等價而不是等同性。如果要檢查引用,請使用is方法。

集合

Groovy有集合的本地文法。使用方括号和逗号分隔值以建立ArrayList。您可以使用as運算符将一種集合類型轉換為另一種集合類型。集合還具有運算符重載,實作類似加号,減号和乘法的方法(示例A-8)。

執行個體A-8。集合示例和方法

def nums = [3, 1, 4, 1, 5, 9, 2, 6, 5]
assert nums instanceof ArrayList
Set uniques = nums as Set
assert uniques == [3, 1, 4, 5, 9, 2, 6] as Set
def sorted = nums as SortedSet
assert sorted == [1, 2, 3, 4, 5, 6, 9] as SortedSet
assert sorted instanceof TreeSet
assert nums[0] == 3
assert nums[1] == 1
assert nums[-1] == 5 // end of list
assert nums[-2] == 6
assert nums[0..3] == [3, 1, 4, 1] // two dots is a Range
assert nums[-3..-1] == [2, 6, 5]
assert nums[-1..-3] == [5, 6, 2]
String hello = 'hello'
assert 'olleh' == hello[-1..0] // Strings are collections too
           

Groovy中的範圍由兩個值組成,兩個值由一對點分隔,如from..to。

範圍從起始位置開始擴充,調用每個元素的下一個,直到到達到位置(包括)。

地圖使用冒号表示法将鍵與值分隔開。映射上的方括号運算符是getAt或putAt方法,具體取決于您是通路還是添加值。點運算符類似地重載。詳見實施例A-9。

執行個體A-9。映射執行個體和方法

def map = [a:1, b:2, c:2]
assert map.getClass() == LinkedHashMap
assert map.a == 1 //1
assert map['b'] == 2 //2
assert map.get('c') == 2 //3
           

1過載點放在這裡

2使用putAt方法

3Java仍然工作

關閉 Closures

Groovy有一個叫Closure的類,它代表一個可以像對象一樣使用的代碼塊。把它看作一個匿名方法的主體,這是一個過分簡化,但不是一個壞的開始。

閉包就像一個Java 8 lambda,因為它接受參數并計算一個代碼塊。 Groovy閉包可以修改在它們之外定義的變量,但是Java 8沒有一個名為Lambda的類。

Groovy中的許多方法将閉包作為參數。例如,集合上的每個方法将每個元素提供給一個閉包,并使用它來計算。一個例子在例A-10中。

執行個體A-10。使用Groovy的每個方法和閉包參數

def nums = [3, 1, 4, 1, 5, 9]
def doubles = [] //1
nums.each { n -> //2
doubles << n * 2 //3
}
assert doubles == [6, 2, 8, 2, 10, 18]
           

1空清單

2每個都接受一個參數的閉包,在箭頭之前,這裡稱為n

3左移位運算符附加到集合

修改定義在閉包外部的變量被認為是副作用,而不是好的做法。優選收集方法,稍後讨論。

這是将清單中的值加倍的自然方式,但是有一個更好的替代方法,稱為collect。 collect方法通過對每個元素應用閉包将集合轉換為新的集合。它類似于Java 8中的映射方法,或者隻是将其視為map-filter-reduce過程中的映射操作(示例A-11)。

執行個體A-11。使用Groovy的collect方法來轉換集合

def nums = [3, 1, 4, 1, 5, 9]
def doubles == nums.collect { it * 2 }
assert doubles == [6, 2, 8, 2, 10, 18]
           

當閉包有單個參數(這是預設值),并且不使用arrow運算符給該參數命名時,虛拟名稱預設為單詞it。在這種情況下,collect方法通過在每個元素的閉包中應用* 2建立雙精度集合。

POGOs

隻有屬性和getter和setter的Java類通常稱為普通Java對象或POJO。 Groovy有類似的類叫POGOs。執行個體A-12。

執行個體A-12。一個簡單的POGO

import groovy.transform.Canonical

@Canonical

class Event {

String name

Date when

int priority

}

這個小類實際上有很多功能。對于POGO:

•預設情況下,類是public

•預設情況下,屬性是私有的

•預設情況下,方法是公用的

•為未标記為公共或私有的每個屬性生成Getter和setter方法

•提供了預設構造函數和“基于映射”的構造函數(使用“attribute:value”形式的參數)

此外,此POGO包括@Canonical注釋,觸發抽象文法樹(AST)轉換。 AST轉換以特定方式修改編譯器在編譯過程中建立的文法樹。

@Canonical注釋實際上是另外三個AST轉換的快捷方式:@ToString,@EqualsAndHashCode和@TupleConstructor。每個都做他們的聲音,是以在這種情況下,@Canonical注釋添加到這個類:

•toString重寫,顯示類的完全限定名稱,後面是屬性的值,按從上到下的順序

•等價重寫,對每個屬性的等價性進行零安全檢查

•hashCode覆寫,以類似于Joshua Bloch在他的Effective Java(Addison-Wesley)書中很久以前的方式基于屬性的值生成整數

•一個附加的構造函數,它将屬性作為參數,按順序

這是七行代碼的生産力。例A-13顯示了如何使用它。

執行個體A-13。使用事件POGO

Event e1 = new Event(name: 'Android Studio 1.0',
when: Date.parse('MMM dd, yyyy', 'Dec 8, 2014'),
priority: 1)
Event e2 = new Event(name: 'Android Studio 1.0',
when: Date.parse('MMM dd, yyyy', 'Dec 8, 2014'),
priority: 1)
assert e1.toString() ==
'Event(Android Studio 1.0, Mon Dec 08 00:00:00 EST 2014, 1)'
assert e1 == e2
Set events = [e1, e2]
assert events.size() == 1
           

Gradle使用所有這些功能,更多,但這個摘要應該讓你開始。

Gradov中的Groovy建構檔案

Gradle建構檔案支援所有Groovy文法。這裡有一些具體的例子,但是,說明Groovy在Gradle。

在示例A-14中,單詞apply是Project執行個體上的一個方法。方法上的括号是可選的,在此省略。參數是在Project執行個體上設定一個名為plugin的屬性到提供的字元串值。

執行個體A-14。為Gradle應用Android插件

apply plugin: 'com.android.application'
           

在示例A-15中,術語android是插件的DSL的一部分,它将閉包作為參數。閉包内的屬性,如compileSdkVersion,是帶有可選括号的方法調用。在一些Gradle建構檔案中,使用=配置設定屬性,這将調用相應的setter方法。 Android插件的開發人員經常添加一個正常方法,如compileSdkVersion(23),除了setter,setCompileSdkVersion(23)。

執行個體A-15。在android塊中設定屬性

android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
}
           

此外,可以使用點表示法來設定“嵌套”屬性(如compileSdkVersion此處):

android.compileSdkVersion = 23

兩者都是等價的。

最新版本的插件為Gradle建構檔案添加了一個幹淨的任務。此任務名稱為clean,是Delete類的一個執行個體(作為任務的子類),并接受一個

閉包作為參數。與标準Groovy實踐保持一緻,閉包顯示在括号之後(示例A-16)。

執行個體A-16。預設清潔任務

task clean(type: Delete) {
delete rootProject.buildDir
}
           

如果Groovy方法使用Closure作為其最後一個參數,則通常在括号後添加閉包。

這裡的實作在rootProject.buildDir上調用delete方法(再次使用可選的括号)。 rootProject屬性的值是頂級項目,buildDir的預設值是“build”,是以此任務将删除頂級項目中的“build”目錄。

注意,在頂層項目中調用clean也會在app子項目上調用它,這将删除那裡的建構目錄。

在示例A-17中,編譯期是DSL的一部分,這意味着它的參數在編譯階段應用。 fileTree方法用括号顯示,盡管它們可以省略。 dir參數接受一個表示本地目錄的字元串。 include參數接受檔案模式的Groovy清單(方括号)。

執行個體A-17。檔案樹依賴關系

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}