什么是 Groovy谈跛?
簡(jiǎn)言之央拖,Groovy是一種基于JVM(Java虛擬機(jī))的敏捷動(dòng)態(tài)開發(fā)語言胶惰。它是一種成熟的面向?qū)ο?/a>編程語言,既可以用于面向?qū)ο缶幊烫舾瘢挚梢杂米骷兇獾?a target="_blank" rel="nofollow">腳本語言咙冗。作為Java程序員,即便以前沒有接觸過Groovy漂彤,也可以快速學(xué)習(xí)雾消。
Groovy 和 Java 語言對(duì)比
用 Java 編寫的典型的 Hello World 示例如下所示:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("java:Hello World");
? ?}
}
用 Groovy 編寫的 Hello World
Groovy 支持松散的 Java 語法 — 例如,不需要為打印 “Hello World!” 這樣的簡(jiǎn)單操作定義類挫望。
而且立润,Groovy 使日常的編碼活動(dòng)變得更容易,例如媳板,Groovy 允許輸入println桑腮,而無需輸入System.out.println。當(dāng)您輸入println時(shí)蛉幸,Groovy 會(huì)非常聰明地知道您指的是System.out破讨。
所以丛晦,用 Groovy 編寫 Hello World 程序就如下面這樣簡(jiǎn)單:
println "Hello World!"
事實(shí)證明,使用Groovy可以達(dá)到事半功倍的效果提陶!
? ?本教程采用的是在eclipse中安裝Groovy插件烫沙,(具體安裝方法見百度),通過建立Groovy項(xiàng)目隙笆,和Java實(shí)例進(jìn)行比較來學(xué)習(xí)锌蓄。
基礎(chǔ)
Groovy注釋標(biāo)記和Java一樣,支持//或者//
Groovy語句可以不用分號(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){//無需指定參數(shù)類型}
除了變量定義可以不指定類型外昼捍,Groovy中函數(shù)的返回值也可以是無類型的。比如:
//無類型的函數(shù)定義肢扯,必須使用def關(guān)鍵字
def nonReturnTypeFunc(){?
?? last_line//最后一行代碼的執(zhí)行結(jié)果就是本函數(shù)的返回值
}
//如果指定了函數(shù)返回類型妒茬,則可不必加def關(guān)鍵字來定義函數(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ō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)容則和腳本語言的處理有點(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"
注意,雖然寫代碼的時(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è)人覺得如果這個(gè)函數(shù)是Groovy API或者Gradle API中比較常用的邓尤,比如println,就可以不帶括號(hào)。否則還是帶括號(hào)裁赠。
Groovy類:
1殿漠,不需要public修飾
2赴精,不需要類型說明
3佩捞,不需要get/set方法
4,不需要構(gòu)造函數(shù)
5蕾哟,不需要return——默認(rèn)返回方法中的最后一行
6一忱,方法調(diào)用時(shí)可以省略() ? //構(gòu)造函數(shù)除外
Groovy 是沒有類型的 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ì)象的值來判斷它的類型肠骆。
將 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)括起來的。
再來看如下賦值:
def message = 12
println message.class
message變量的數(shù)字值看起來像是 Java 的原生類型int磁玉。但是停忿,運(yùn)行這個(gè)代碼就可以看出,Groovy 將它作為Integer蜀涨。這是因?yàn)樵?Groovy 中 “一切都是對(duì)象”瞎嬉。
Java 中的所有對(duì)象都擴(kuò)展自java.lang.Object,這對(duì) Groovy 來說非常方便厚柳。即使在最糟的情況下氧枣,Groovy 運(yùn)行時(shí)不能確定變量的類型,它只需將變量當(dāng)成Object别垮,問題就解決了便监。
繼續(xù)使用這段代碼。將message改成自己喜歡的任意類型:Groovy 會(huì)在運(yùn)行時(shí)盡其所能推斷出這個(gè)變量的類型。
無類型有什么意義烧董?
Groovy 缺少類型意味著所需的輸入更少毁靶。更重要的是,這意味著要閱讀的代碼要少得多逊移。最后预吆,Groovy 缺少類型能夠帶來更高的靈活性 — 不需要接口或抽象類。
所以胳泉,只需要使用def關(guān)鍵字就能在方法中聲明一個(gè)獨(dú)立變量拐叉,不需要將def關(guān)鍵字作為方法聲明中的參數(shù)。在for循環(huán)聲明中也不需要它扇商,這意味著不用編寫(int x = 0; x < 5; x++)凤瘦,相反,可以省略int案铺,保留空白蔬芥。
通過 Groovy 進(jìn)行循環(huán)
同大多數(shù)腳本語言一樣,Groovy 經(jīng)常被宣傳為生產(chǎn)力更高的 Java 語言替代品控汉。
首先笔诵,用與創(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 的角度來看,這個(gè)小函數(shù)看起來可能有些怪(實(shí)際上壁酬,它很像 JavaScript)次酌。但它就是 Java 代碼,只不過是用 Groovy 的樣式編寫的舆乔。
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璃搜。沒有變量類型的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)帶來了很大的方便疚顷。例如,前面從 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è)小問題:“Hello World” 輸出了六次而不是五次。這個(gè)問題有三種解決方法:
將包含的范圍限制到 4: ?for(i in 0..4)
從 1 而不是 0 開始: ?for(i in 1..5)
將范圍由包含改為排除:for(i in 0..<5)
不論采用哪種方法如暖,都會(huì)得到原來的效果 — 輸出 “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)過時(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 可以直接在語言內(nèi)使用集合匆篓。在 Groovy 中,不需要導(dǎo)入專門的類寇窑,也不需要初始化對(duì)象鸦概。集合是語言本身的本地成員。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ì)象看起來很像 Java 語言中的數(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)箕憾。還可以通過位置參數(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)目全部變成大寫,可以編寫以下代碼:
assert ["JAVA", "GROOVY"] ==["Java", "Groovy"]*.toUpperCase()
請(qǐng)注意*.標(biāo)記壳炎。對(duì)于以上列表中的每個(gè)值泞歉,都會(huì)調(diào)用toUpperCase(),生成的集合中每個(gè)String實(shí)例都是大寫的匿辩。
Groovy 映射
除了豐富的列表處理功能腰耙,Groovy 還提供了堅(jiān)固的映射機(jī)制。同列表一樣铲球,映射也是本地?cái)?shù)據(jù)結(jié)構(gòu)挺庞。而且 Groovy 中的任何映射機(jī)制在幕后都是java.util.Map的實(shí)例。
Java 語言中的映射是名稱-值對(duì)的集合睬辐。所以挠阁,要用 Java 代碼創(chuàng)建典型的映射,必須像下面這樣操作:
Map map = new HashMap();
map.put("name", "Andy");
map.put("VPN-#","45");
Groovy 使得處理映射的操作像處理列表一樣簡(jiǎn)單 — 例如溯饵,可以用 Groovy 將上面的 Java 映射寫成
def hash = [name:"Andy", "VPN-#":45]
請(qǐng)注意,Groovy 映射中的鍵不必是String锨用。在這個(gè)示例中丰刊,name看起來像一個(gè)變量,但是在幕后增拥,Groovy 會(huì)將它變成String啄巧。
全都是 Java
接下來創(chuàng)建一個(gè)新類Mapper并加入上面的代碼。然后添加以下代碼掌栅,以證實(shí)底層的代碼是真正的 Java 代碼:
assert hash.getClass() == java.util.LinkedHashMap
可以看到 Groovy 使用了 Java 的LinkedHashMap類型秩仆,這意味著可以使用標(biāo)準(zhǔn)的 Java 一樣語句對(duì)hash中的項(xiàng)執(zhí)行put和get操作。
hash.put("id", 23)
assert hash.get("name") == "Andy"
有 groovy 特色的映射
hash.dob = "01/29/76"
.符號(hào)還可以用來獲取項(xiàng)猾封。例如澄耍,使用以下方法可以獲取dob的值:
assert hash.dob == "01/29/76"
Groovy 中的閉包
不再需要更多迭代
雖然在前幾節(jié)編寫了不少集合代碼,但還沒有實(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)接下來要放在閉包內(nèi)。在深入之前播玖,先看看如何執(zhí)行這步操作椎工。
能否看見閉包?
對(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)用之后,引入了一種新的語法 —{局待,然后是一些代碼斑响,然后是}。由{}包圍起來的代碼塊就是閉包钳榨。
執(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饼齿。
迭代無處不在
閉包在 Groovy 中頻繁出現(xiàn)饲漾,但是,通常用于在一系列值上迭代的時(shí)候缕溉。請(qǐng)記住考传,一系列值可以用多種方式表示,不僅可以用列表表示 — 例如证鸥,可以在映射僚楞、String、JDBCRowset敌土、File的行上迭代镜硕,等等。
對(duì)于前面的例子返干,可以編寫以下代碼:
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)合上特別有用。
例如美侦,通過 Eclipse 創(chuàng)建一個(gè)ClosureExample對(duì)象产舞,并保持它提供的默認(rèn)類語法。在生成的main()方法中菠剩,添加以下代碼:
def excite = { word ->
return "${word}!!"
}
這段代碼是名為excite的閉包易猫。這個(gè)閉包接受一個(gè)參數(shù)(名為word),返回的String是word變量加兩個(gè)感嘆號(hào)具壮。請(qǐng)注意在String實(shí)例中替換的用法准颓。在String中使用${value}語法將告訴 Groovy 替換String中的某個(gè)變量的值」准耍可以將這個(gè)語法當(dāng)成return word + "!!"的快捷方式攘已。
延遲執(zhí)行
assert "Groovy!!" == excite("Groovy")
ssert "Java!!" == excite.call("Java")
可以看到,兩種調(diào)用方式都能工作怜跑,但是直接調(diào)用的方法更簡(jiǎn)潔贯被。
文件I/O操作
直接來看例子吧,雖然比Java看起來簡(jiǎn)單妆艘,但要理解起來其實(shí)比較難。尤其是當(dāng)你要自己查SDK并編寫代碼的時(shí)候看幼。
整體說來批旺,Groovy的I/O操作是在原有Java I/O操作上進(jìn)行了更為簡(jiǎn)單方便的封裝,并且使用Closure來簡(jiǎn)化代碼編寫诵姜。主要封裝了如下一些了類:
讀文件
Groovy中汽煮,文件讀操作簡(jiǎn)單到令人發(fā)指:
def targetFile = new File(文件名) <==File對(duì)象還是要?jiǎng)?chuàng)建的。
讀該文件中的每一行:eachLine的唯一參數(shù)是一個(gè)Closure棚唆。Closure的參數(shù)是文件每一行的內(nèi)容
寫文件:
結(jié)束語
通過本篇的學(xué)習(xí)暇赤,進(jìn)一步認(rèn)識(shí)到 Groovy 就是 Java,只是缺少了過去使用Java的許多語法規(guī)則宵凌。Groovy 是沒有類型鞋囊、沒有修改符、沒有 return瞎惫、沒有Iterator溜腐、不需要導(dǎo)入集合的 Java译株。簡(jiǎn)而言之,Groovy 就是丟掉了許多包袱的 Java挺益,這些包袱可能會(huì)壓垮 Java 項(xiàng)目歉糜。
但是在幕后,Groovy 就是 Java望众。