groovy學(xué)習(xí)之路

什么是 Groovy?


簡(jiǎn)言之,Groovy是一種基于JVMJava虛擬機(jī))的敏捷動(dòng)態(tài)開(kāi)發(fā)語(yǔ)言。它是一種成熟的面向?qū)ο?/a>編程語(yǔ)言,既可以用于面向?qū)ο缶幊蹋挚梢杂米骷兇獾?a target="_blank" rel="nofollow">腳本語(yǔ)言。作為Java程序員,即便以前沒(méi)有接觸過(guò)Groovy,也可以快速學(xué)習(xí)。

Groovy 和 Java 語(yǔ)言對(duì)比

用 Java 編寫(xiě)的典型的 Hello World 示例如下所示:

public class HelloWorld {

public static void main(String[] args) {

System.out.println("java:Hello World");

? ?}

}

用 Groovy 編寫(xiě)的 Hello World

Groovy 支持松散的 Java 語(yǔ)法 — 例如,不需要為打印 “Hello World!” 這樣的簡(jiǎn)單操作定義類。

而且,Groovy 使日常的編碼活動(dòng)變得更容易,例如,Groovy 允許輸入println,而無(wú)需輸入System.out.println。當(dāng)您輸入println時(shí),Groovy 會(huì)非常聰明地知道您指的是System.out。

所以,用 Groovy 編寫(xiě) Hello World 程序就如下面這樣簡(jiǎn)單:

println "Hello World!"

事實(shí)證明,使用Groovy可以達(dá)到事半功倍的效果!

? ?本教程采用的是在eclipse中安裝Groovy插件,(具體安裝方法見(jiàn)百度),通過(guò)建立Groovy項(xiàng)目,和Java實(shí)例進(jìn)行比較來(lái)學(xué)習(xí)。

基礎(chǔ)

Groovy注釋標(biāo)記和Java一樣,支持//或者//

Groovy語(yǔ)句可以不用分號(hào)結(jié)尾。

Groovy中支持動(dòng)態(tài)類型,即定義變量的時(shí)候可以不指定其類型。Groovy中,變量定義可以使用關(guān)鍵字def

def var=1

def str="iamaperson"

def intx=1 ?//也可以指定類型

函數(shù)定義時(shí),參數(shù)的類型也可以不指定。比如

Stringfunction(arg1,args2){//無(wú)需指定參數(shù)類型}

除了變量定義可以不指定類型外,Groovy中函數(shù)的返回值也可以是無(wú)類型的。比如:

//無(wú)類型的函數(shù)定義,必須使用def關(guān)鍵字

def nonReturnTypeFunc(){?

?? last_line//最后一行代碼的執(zhí)行結(jié)果就是本函數(shù)的返回值

}

//如果指定了函數(shù)返回類型,則可不必加def關(guān)鍵字來(lái)定義函數(shù)

String getString(){

return "I am a string"

}

Groovy是基于Java的,而且最終會(huì)轉(zhuǎn)成Java Code運(yùn)行在JVM上

Groovy對(duì)字符串支持相當(dāng)強(qiáng)大,充分吸收了一些腳本語(yǔ)言的優(yōu)點(diǎn):

1 單引號(hào)''中的內(nèi)容嚴(yán)格對(duì)應(yīng)Java中的String,不對(duì)$符號(hào)進(jìn)行轉(zhuǎn)義

def ? singleQuote='Iam$dolloar' ? ? ? ? ?//輸出就是Iam$dolloar

2 雙引號(hào)""的內(nèi)容則和腳本語(yǔ)言的處理有點(diǎn)像,如果字符中有$號(hào)的話,則它會(huì)$表達(dá)式先求值。

def ? doubleQuoteWithoutDollar="I am one dollar" ? ?//輸出 I am one dollar

def x=1

def ? doubleQuoteWithDollar="I am $x dolloar" ? ? ? ?//輸出I am 1 dolloar

3 三個(gè)引號(hào)'''xxx'''中的字符串支持隨意換行 比如

def multieLines =''' begin

line? 1

line? 2

end '''

最后,除了每行代碼不用加分號(hào)外,Groovy中函數(shù)調(diào)用的時(shí)候還可以不加括號(hào)。比如:

println("test")---> println ?"test"

注意,雖然寫(xiě)代碼的時(shí)候,對(duì)于函數(shù)調(diào)用可以不帶括號(hào),但是Groovy經(jīng)常把屬性和函數(shù)調(diào)用混淆。比如

def ?getSomething(){"hello"}

get ? Something() //如果不加括號(hào)的話,Groovy會(huì)誤認(rèn)為getSomething是一個(gè)變量。

所以,調(diào)用函數(shù)要不要帶括號(hào),個(gè)人覺(jué)得如果這個(gè)函數(shù)是Groovy API或者Gradle API中比較常用的,比如println,就可以不帶括號(hào)。否則還是帶括號(hào)。

Groovy類:

1,不需要public修飾

2,不需要類型說(shuō)明

3,不需要get/set方法

4,不需要構(gòu)造函數(shù)

5,不需要return——默認(rèn)返回方法中的最后一行

6,方法調(diào)用時(shí)可以省略() ? //構(gòu)造函數(shù)除外

Groovy 是沒(méi)有類型的 Java 代碼

在 Java 中,如果要聲明一個(gè)String變量,則必須輸入:

String value = "Hello World";

但是,如果仔細(xì)想想,就會(huì)看出,等號(hào)右側(cè)的字符已經(jīng)表明value的類型是String。所以,Groovy 允許省略value前面的String類型變量,并用def代替。

def value = "Hello World"

實(shí)際上,Groovy 會(huì)根據(jù)對(duì)象的值來(lái)判斷它的類型。

將 HelloWorld.groovy 文件中的代碼編輯成下面這樣:

String message = "Hello World"

println message

運(yùn)行這段代碼,應(yīng)該會(huì)在控制臺(tái)上看到與前面一樣的 “Hello World”。現(xiàn)在,將變量類型String替換為def并重新運(yùn)行代碼。是不是注意到了相同的結(jié)果?

除了輸出message的值,還可以用以下調(diào)用輸出它的類型:

println message.class

輸出 “class java.lang.String” ? ? ? Groovy 推斷出message一定是String類型的,因?yàn)樗闹凳怯秒p引號(hào)括起來(lái)的。

再來(lái)看如下賦值:

def message = 12

println message.class

message變量的數(shù)字值看起來(lái)像是 Java 的原生類型int。但是,運(yùn)行這個(gè)代碼就可以看出,Groovy 將它作為Integer。這是因?yàn)樵?Groovy 中 “一切都是對(duì)象”。

Java 中的所有對(duì)象都擴(kuò)展自java.lang.Object,這對(duì) Groovy 來(lái)說(shuō)非常方便。即使在最糟的情況下,Groovy 運(yùn)行時(shí)不能確定變量的類型,它只需將變量當(dāng)成Object,問(wèn)題就解決了。

繼續(xù)使用這段代碼。將message改成自己喜歡的任意類型:Groovy 會(huì)在運(yùn)行時(shí)盡其所能推斷出這個(gè)變量的類型。

無(wú)類型有什么意義?

Groovy 缺少類型意味著所需的輸入更少。更重要的是,這意味著要閱讀的代碼要少得多。最后,Groovy 缺少類型能夠帶來(lái)更高的靈活性 — 不需要接口或抽象類。

所以,只需要使用def關(guān)鍵字就能在方法中聲明一個(gè)獨(dú)立變量,不需要將def關(guān)鍵字作為方法聲明中的參數(shù)。在for循環(huán)聲明中也不需要它,這意味著不用編寫(xiě)(int x = 0; x < 5; x++),相反,可以省略int,保留空白。

通過(guò) Groovy 進(jìn)行循環(huán)

同大多數(shù)腳本語(yǔ)言一樣,Groovy 經(jīng)常被宣傳為生產(chǎn)力更高的 Java 語(yǔ)言替代品。

首先,用與創(chuàng)建HelloWorld相同的方式創(chuàng)建一個(gè) Groovy 類,并刪除自動(dòng)生成的類體:將要定義一個(gè)獨(dú)立的repeat函數(shù)。現(xiàn)在在控制臺(tái)中輸入以下代碼:

def ?repeat(val){

for(i = 0; i < 5; i++){

println val

}

}

起初,從 Java 的角度來(lái)看,這個(gè)小函數(shù)看起來(lái)可能有些怪(實(shí)際上,它很像 JavaScript)。但它就是 Java 代碼,只不過(guò)是用 Groovy 的樣式編寫(xiě)的。

repeat函數(shù)接受一個(gè)變量val。請(qǐng)注意參數(shù)不需要def。方法體本質(zhì)上就是一個(gè)for循環(huán)。

調(diào)用這個(gè)函數(shù)。

repeat("hello?world")

會(huì)輸出 “hello world” 五次。請(qǐng)注意,for循環(huán)中省略了int。沒(méi)有變量類型的for循環(huán)要比標(biāo)準(zhǔn)的 Java 代碼短些。現(xiàn)在看看如果在代碼里加入范圍會(huì)出現(xiàn)什么情況。

Groovy 中的范圍

范圍是一系列的值。例如 “0..4” 表明包含整數(shù) 0、1、2、3、4。Groovy 還支持排除范圍,“0..<4” 表示 0、1、2、3。還可以創(chuàng)建字符范圍:“a..e” 相當(dāng)于 a、b、c、d、e。“a..e的所有值。

循環(huán)范圍

范圍為循環(huán)帶來(lái)了很大的方便。例如,前面從 0 遞增到 4 的for循環(huán)如下所示:

for(i = 0; i < 5; i++)

范圍可以將這個(gè)for循環(huán)變得更簡(jiǎn)潔,更易閱讀:

def repeat(val){

for(i in 0..5){

println val

}

}

設(shè)置范圍

如果運(yùn)行這個(gè)示例,可能會(huì)注意到一個(gè)小問(wèn)題:“Hello World” 輸出了六次而不是五次。這個(gè)問(wèn)題有三種解決方法:

將包含的范圍限制到 4: ?for(i in 0..4)

從 1 而不是 0 開(kāi)始: ?for(i in 1..5)

將范圍由包含改為排除:for(i in 0..<5)

不論采用哪種方法,都會(huì)得到原來(lái)的效果 — 輸出 “Hello World” 五次。

默認(rèn)參數(shù)值

現(xiàn)在已經(jīng)成功地使用 Groovy 的范圍表達(dá)式縮短了repeat函數(shù)。但這個(gè)函數(shù)依然有些限制。如果想重復(fù) “Hello World” 八次該怎么辦?如果想對(duì)不同的值重復(fù)不同次數(shù) — 比如 “Hello World” 重復(fù)八次,“Goodbye Sunshine” 重復(fù)兩次,這時(shí)該怎么辦?

每次調(diào)用repeat時(shí)都要指定需要的重復(fù)次數(shù)的做法已經(jīng)過(guò)時(shí)了,特別是在已經(jīng)適應(yīng)了默認(rèn)行為(重復(fù)五次)的時(shí)候。

Groovy 支持默認(rèn)參數(shù)值,可以在函數(shù)或方法的正式定義中指定參數(shù)的默認(rèn)值。調(diào)用函數(shù)的程序可以選擇省略參數(shù),使用默認(rèn)值。

def repeat(val, repeat=5){

for(i in 0..repeat){

println val

}

}

像下面這樣調(diào)用該函數(shù):

repeat("Hello World", 2)

repeat("Goodbye sunshine", 4)

repeat("foo")

結(jié)果會(huì)輸出 “Hello World” 兩次,“Goodbye sunshine” 四次,“foo” 五次(默認(rèn)次數(shù))。

Groovy 集合

在 Groovy 提供的所有方便的快捷方式和功能中,最有幫助的一個(gè)可能就是內(nèi)置的集合。在 Java 編程中使用集合— 導(dǎo)入java.util類,初始化集合,將項(xiàng)加入集合。這三個(gè)步驟都會(huì)增加不少代碼。

而 Groovy 可以直接在語(yǔ)言內(nèi)使用集合。在 Groovy 中,不需要導(dǎo)入專門(mén)的類,也不需要初始化對(duì)象。集合是語(yǔ)言本身的本地成員。Groovy 也使集合(或者列表)的操作變得非常容易,為增加和刪除項(xiàng)提供了直觀的幫助。

def range = 0..4 ? ? //把范圍當(dāng)集合

println range.class

assert range instanceof List ? //證明了range是集合

Groovy 的集合支持相當(dāng)豐富,而且美妙之處就在于,在 Groovy 的魔法背后,一切都是標(biāo)準(zhǔn)的 Java 對(duì)象。每個(gè) Groovy 集合都是java.util.Collection或java.util.Map的實(shí)例。

def coll = ["Groovy", "Java", "Ruby"]

assert? coll instanceof Collection

assert coll instanceof ArrayList

你將會(huì)注意到,coll對(duì)象看起來(lái)很像 Java 語(yǔ)言中的數(shù)組。實(shí)際上,它是一個(gè)Collection。要在普通的 Java 代碼中得到集合的相同實(shí)例,必須執(zhí)行以下操作:

Collection coll = new ArrayList();

coll.add("Groovy");

coll.add("Java");

coll.add("Ruby");

在 Java 代碼中,必須使用add()方法向ArrayList實(shí)例添加項(xiàng)。

Groovy 提供了許多方法可以將項(xiàng)添加到列表 — 可以使用add()方法(因?yàn)榈讓拥募鲜且粋€(gè)普通的ArrayList類型),但是還有許多快捷方式可以使用。

例如:

coll.add("Python")

coll << "Smalltalk"

coll[5] = "Perl"

請(qǐng)注意,Groovy 支持操作符重載 —<<操作符被重載,以支持向集合添加項(xiàng)。還可以通過(guò)位置參數(shù)直接添加項(xiàng)。在這個(gè)示例中,由于集合中只有四個(gè)項(xiàng),所以[5]操作符將 “Perl” 放在最后。請(qǐng)自行輸出這個(gè)集合并查看效果。

Groovy 還允許在集合中增加或去掉集合,如下所示:

def numbers = [1,2,3,4]

assert numbers + 5 == [1,2,3,4,5]

assert numbers - [2,3] == [1,4]

在上面的代碼中, 實(shí)際上創(chuàng)建了新的集合實(shí)例,由最后一行可以看出。

魔法方法

Groovy 還為集合添加了其他一些方便的功能。例如,可以在集合實(shí)例上調(diào)用特殊的方法,如下所示:

def numbers = [1,2,3,4]

assert numbers.join(",") == "1,2,3,4"

assert [1,2,3,4,3].count(3) == 2

join()和count()只是在任何項(xiàng)列表上都可以調(diào)用的眾多方便方法中的兩個(gè)。分布操作符(spread operator)是個(gè)特別方便的工具,使用這個(gè)工具不用在集合上迭代,就能夠調(diào)用集合的每個(gè)項(xiàng)上的方法。

假設(shè)有一個(gè)String列表,現(xiàn)在想將列表中的項(xiàng)目全部變成大寫(xiě),可以編寫(xiě)以下代碼:

assert ["JAVA", "GROOVY"] ==["Java", "Groovy"]*.toUpperCase()

請(qǐng)注意*.標(biāo)記。對(duì)于以上列表中的每個(gè)值,都會(huì)調(diào)用toUpperCase(),生成的集合中每個(gè)String實(shí)例都是大寫(xiě)的。

Groovy 映射

除了豐富的列表處理功能,Groovy 還提供了堅(jiān)固的映射機(jī)制。同列表一樣,映射也是本地?cái)?shù)據(jù)結(jié)構(gòu)。而且 Groovy 中的任何映射機(jī)制在幕后都是java.util.Map的實(shí)例。

Java 語(yǔ)言中的映射是名稱-值對(duì)的集合。所以,要用 Java 代碼創(chuàng)建典型的映射,必須像下面這樣操作:

Map map = new HashMap();

map.put("name", "Andy");

map.put("VPN-#","45");

Groovy 使得處理映射的操作像處理列表一樣簡(jiǎn)單 — 例如,可以用 Groovy 將上面的 Java 映射寫(xiě)成

def hash = [name:"Andy", "VPN-#":45]

請(qǐng)注意,Groovy 映射中的鍵不必是String。在這個(gè)示例中,name看起來(lái)像一個(gè)變量,但是在幕后,Groovy 會(huì)將它變成String。

全都是 Java

接下來(lái)創(chuàng)建一個(gè)新類Mapper并加入上面的代碼。然后添加以下代碼,以證實(shí)底層的代碼是真正的 Java 代碼:

assert hash.getClass() == java.util.LinkedHashMap

可以看到 Groovy 使用了 Java 的LinkedHashMap類型,這意味著可以使用標(biāo)準(zhǔn)的 Java 一樣語(yǔ)句對(duì)hash中的項(xiàng)執(zhí)行put和get操作。

hash.put("id", 23)

assert hash.get("name") == "Andy"

有 groovy 特色的映射

hash.dob = "01/29/76"

.符號(hào)還可以用來(lái)獲取項(xiàng)。例如,使用以下方法可以獲取dob的值:

assert hash.dob == "01/29/76"

Groovy 中的閉包

不再需要更多迭代

雖然在前幾節(jié)編寫(xiě)了不少集合代碼,但還沒(méi)有實(shí)際地在集合上迭代。當(dāng)然,您知道 Groovy 就是 Java,所以如果愿意,那么總是能夠得到 Java 的Iterator實(shí)例,用它在集合上迭代,就像下面這樣:

def acoll = ["Groovy", "Java", "Ruby"]

for(Iterator iter = acoll.iterator(); iter.hasNext();){

println iter.next()

}

實(shí)際上在for循環(huán)中并不需要類型聲明,因?yàn)?Groovy 已經(jīng)將迭代轉(zhuǎn)變?yōu)槿魏渭系闹苯映蓡T。在這個(gè)示例中,不必獲取Iterator實(shí)例并直接操縱它,可以直接在集合上迭代。而且,通常放在循環(huán)構(gòu)造內(nèi)的行為(例如for循環(huán)體中println)接下來(lái)要放在閉包內(nèi)。在深入之前,先看看如何執(zhí)行這步操作。

能否看見(jiàn)閉包?

對(duì)于上面的代碼,可以用更簡(jiǎn)潔的方式對(duì)集合進(jìn)行迭代,如下所示:

def acoll = ["Groovy", "Java", "Ruby"]

acoll.each{

println it

}

請(qǐng)注意,each直接在acoll實(shí)例內(nèi)調(diào)用,而acoll實(shí)例的類型是ArrayList。在each調(diào)用之后,引入了一種新的語(yǔ)法 —{,然后是一些代碼,然后是}。由{}包圍起來(lái)的代碼塊就是閉包

執(zhí)行代碼:

閉包中的it變量是一個(gè)關(guān)鍵字,指向被調(diào)用的外部集合的每個(gè)值 — 它是默認(rèn)值,可以用傳遞給閉包的參數(shù)覆蓋它。下面的代碼執(zhí)行同樣的操作,但使用自己的項(xiàng)變量:

def acoll = ["Groovy", "Java", "Ruby"]

acoll.each{ value ->

println value

}

在這個(gè)示例中,用value代替了 Groovy 的默認(rèn)it。

迭代無(wú)處不在

閉包在 Groovy 中頻繁出現(xiàn),但是,通常用于在一系列值上迭代的時(shí)候。請(qǐng)記住,一系列值可以用多種方式表示,不僅可以用列表表示 — 例如,可以在映射、String、JDBCRowset、File的行上迭代,等等。

對(duì)于前面的例子,可以編寫(xiě)以下代碼:

def hash = [name:"Andy", "VPN-#":45]

hash.each{ key, value ->

println "${key} : ${value}"

}

請(qǐng)注意,閉包還允許使用多個(gè)參數(shù) — 在這個(gè)示例中,上面的代碼包含兩個(gè)參數(shù)(key和value)。

使用 Java 代碼迭代

Mapmap = new HashMap();

map.put("name", "Andy");

map.put("VPN-#","45");

for(Iterator iter = map.entrySet().iterator(); iter.hasNext();){

Map.Entry entry = (Map.Entry)iter.next();

System.out.println(entry.getKey() + " : " + entry.getValue());

}

很明顯,上面的代碼比 Groovy 的代碼長(zhǎng)得多,如果要處理大量集合,那么顯然用 Groovy 處理會(huì)更方便。

閉包的更多使用方式

雖然在迭代上使用閉包的機(jī)會(huì)最多,但閉包確實(shí)還有其他用途。因?yàn)殚]包是一個(gè)代碼塊,所以能夠作為參數(shù)進(jìn)行傳遞(Groovy 中的函數(shù)或方法不能這樣做)。閉包在調(diào)用的時(shí)候才會(huì)執(zhí)行這一事實(shí)(不是在定義的時(shí)候)使得它們?cè)谀承﹫?chǎng)合上特別有用。

例如,通過(guò) Eclipse 創(chuàng)建一個(gè)ClosureExample對(duì)象,并保持它提供的默認(rèn)類語(yǔ)法。在生成的main()方法中,添加以下代碼:

def excite = { word ->

return "${word}!!"

}

這段代碼是名為excite的閉包。這個(gè)閉包接受一個(gè)參數(shù)(名為word),返回的String是word變量加兩個(gè)感嘆號(hào)。請(qǐng)注意在String實(shí)例中替換的用法。在String中使用${value}語(yǔ)法將告訴 Groovy 替換String中的某個(gè)變量的值。可以將這個(gè)語(yǔ)法當(dāng)成return word + "!!"的快捷方式。

延遲執(zhí)行

assert "Groovy!!" == excite("Groovy")

ssert "Java!!" == excite.call("Java")

可以看到,兩種調(diào)用方式都能工作,但是直接調(diào)用的方法更簡(jiǎn)潔。

文件I/O操作

直接來(lái)看例子吧,雖然比Java看起來(lái)簡(jiǎn)單,但要理解起來(lái)其實(shí)比較難。尤其是當(dāng)你要自己查SDK并編寫(xiě)代碼的時(shí)候。

整體說(shuō)來(lái),Groovy的I/O操作是在原有Java I/O操作上進(jìn)行了更為簡(jiǎn)單方便的封裝,并且使用Closure來(lái)簡(jiǎn)化代碼編寫(xiě)。主要封裝了如下一些了類:


讀文件

Groovy中,文件讀操作簡(jiǎn)單到令人發(fā)指:

def targetFile = new File(文件名) <==File對(duì)象還是要?jiǎng)?chuàng)建的。

讀該文件中的每一行:eachLine的唯一參數(shù)是一個(gè)Closure。Closure的參數(shù)是文件每一行的內(nèi)容

寫(xiě)文件:

結(jié)束語(yǔ)

通過(guò)本篇的學(xué)習(xí),進(jìn)一步認(rèn)識(shí)到 Groovy 就是 Java,只是缺少了過(guò)去使用Java的許多語(yǔ)法規(guī)則。Groovy 是沒(méi)有類型、沒(méi)有修改符、沒(méi)有 return、沒(méi)有Iterator、不需要導(dǎo)入集合的 Java。簡(jiǎn)而言之,Groovy 就是丟掉了許多包袱的 Java,這些包袱可能會(huì)壓垮 Java 項(xiàng)目。

但是在幕后,Groovy 就是 Java。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容