Groovy 是一種動態語言,可以輕易的將方法加到原來死板的 Java 類別中,這樣可以帶來很多好處,有時候太有彈性,甚至用起來有一點頭暈腦脹,所以先整理一下一些好用的方法。
先來講 << 。
List rpt_list = new ArrayList()
rpt_list << new Report(name:"as1" , m1:10 , m2:20 , total:30)
rpt_list << new Report(name:"as2" , m1:40 , m2:40 , total:80)
rpt_list << new Report(name:"as3" , m1:20 , m2:30 , total:50)
<< 看起來很奇怪,但說起來也蠻好理解的,就是「塞過去」。
看箭頭的方向也知道,我要把右邊的塞到左邊。這個是 Java 沒有的語法。 你要是看起來很不習慣,一樣還是可以用 add ,這是 Groovy 的好處,你把他想成加強版的 Java 即可。
new Report 右邊的東西,是把屬性像 map 一樣傳進去。冒號的左邊是key右邊是value。還蠻容易理解的吧。
這邊有一個有趣的地方。一般來說 Java 要動態呼叫一個方法或是屬性,有一點麻煩,也就是說我們「執行時期」才知道要呼叫誰的話,在 Java 是比較不直觀的方式處理的。再一次強調,我的觀點仍然認為,
程式的價值不在這些轉換上,所以務必要很容易做到。
既然你可以把 map 當建構子傳入,那你可能會想到他是怎麼動態去mapping。
假設你要自己寫也不難,在 Groovy 來說只要寫
def dynamic_prop="total"
assert rpt_list[0]."${dynamic_prop}" == rpt_list[0]."total"
assert rpt_list[0]."total" == rpt_list[0].total
下面二個斷言都會是 true。相信你已經知道 ${ } 是像jsp 裡的變數,Groovy可放在字串中。
所以
rpt_list[0]."${dynamic_prop}" => 會變成 rpt_list[0]."total"
而造成了可以簡單的動態呼叫的效果。相對於 Java 直接又容易理解。
好了你囉嗦了這麼久,該說 collect 了吧。我們已經準備好剛剛的 List 了
List rpt_list = new ArrayList()
rpt_list << new Report(name:"as1" , m1:10 , m2:20 , total:30)
rpt_list << new Report(name:"as2" , m1:40 , m2:40 , total:80)
rpt_list << new Report(name:"as3" , m1:20 , m2:30 , total:50)
再來你 GSP/JSP 的頁面,老闆要你最上面要先列出一個一個總和。
聰明的你應該想到上一集的招式了
List rpt_total_list = rpt_list*.getTotal()
事情有這麼簡單當然最好,但是老闆改口說,業務要看的是 / 1000 的單位啦,不然值太大。
你抓了抓頭,這樣 「星點」好像不能用了,其實還有更棒的招,就是collect,一行。
List rpt_total_list = rpt_list.collect{ it.total/1000 }
我紅色標起來的是 Closure,大陸翻閉包,很難聽,就還是叫 Closure好了。
這樣說,閉包是一個物件,這個物件裡要寫一些邏輯,包起來當參數餵給 collect。
collect 裡頭,Groovy 自己幫你實做了,你要餵它的就是閉包 ( 好吧…叫閉包也行 )
collect 收到閉包後他做了什麼事 ?
其實 collect 在上例被呼叫了三次,因為rpt_list 裡有三個物件。
這邊依序取這三個物件的 total 後除 1000 ,然後add 到List 中,有多少加多少,加到完之後結束,把 List 傳出來這樣。
如果用傳統的 Java 來寫,又要容易閱讀的話 :
private List<Report> getTotalList(){
List rpt_total_list = new ArrayList();
for(Report r : rpt_list){
rpt_total_list.add(r.getTotal);
}
return rpt_total_list ;
}
//煩死了…一行Groovy比七行Java
閉包使用起來像一個方法,只是不用再獨立寫出來。Groovy 提供了很多對 Collection 的瘋狂技巧。再下來我們講sort。
想到要排序剛剛的sort就很頭痛,因為在 Java 來說,你得這樣寫。你要實作compare來完成這個需求,剛剛的名言又要再講一次,這種東西沒有價值,越好寫越快。價值在客戶要用什麼排序,不是在你怎麼寫出這段程式。
public void sortPeopleByGivenName(List personList) {
Collections.sort(personList, new Comparator() {
public int compare(Person p1, Person p2) {
return p1.getFamilyName().compareTo(p2.getFamilyName());
}
});
}
//很煩吧…
用 Groovy 的話
def sortIt(list, property) {
list.sort { p1, p2 ->
p1."${property}" <=> p2."${property}"
}
}
長的有一點奇怪,不過你呼叫 sortIt ( rpt_list , "total") 就可以得到你想要的結果了。 上面講過了,那個${}會整個被替換成你丟入的參數。你也注意到了 sort 本身就接受閉包,所以不如改成
rpt_list.sort{o1,o2->
o1.total <=> o2.total
}
<=> 是啥啦 !
它就是
public int compare(int i1, int i2) {
if (i1 == i2) return 0;
else if (i1 < i2) return -1;
else return 1;
}
的簡寫啦 ! 所以在閉包裡如果比的是數字,除非你的比法和人家不同,否則 <=> 就行了!
這樣子 sort 你開心了嗎 ?
今天講的就是 collect 與 sort
最後,其實 sort 還可以加條件,剛剛都白寫了 !
沒錯,Groovy的寫法真的太有彈性了,你還可以寫成
rpt_list.sort{ it.getTotal() }
是不是很瘋狂呢? 叫你「按照裡面每一個物件的total 給我排序!」
如果你要排的是數字,最後一個方案就是你要的! 一行搞定。
想哭嗎? 很正常。
如果不是單純的排序,你有一些奇怪規則的,留給你自己,在閉包裡動動腦筋囉!