[轉(zhuǎn)載] Groovy 1.5的新特性
作者 Guillaume Laforge譯者 曹云飛 發(fā)布于 2008年1月15日 下午11時(shí)12分
Groovy,針對(duì)JVM的類Java動(dòng)態(tài)語(yǔ)言,如陳年好酒一樣成熟了。在2007年1月成功地發(fā)布了Groovy 1.0之后,下一個(gè)主要的里程碑1.5版已經(jīng)發(fā)布。在1.5版中有一些有趣而新穎的地方,我們會(huì)在這篇文章中考察這些特性。語(yǔ)言主要增強(qiáng)了對(duì)于Java 5特征的支持,包括注解、泛型和枚舉,這使得Groovy成為對(duì)于JVM完全支持的框架的唯一候選動(dòng)態(tài)語(yǔ)言,框架包括Spring,Hibernate,JPA,Goole Guice或者TestNG。除了新的Java 5特性,Groovy還在語(yǔ)言中增加了新的語(yǔ)法增強(qiáng),以及更強(qiáng)大的動(dòng)態(tài)特性定制,一個(gè)基于steroids的Swing UI構(gòu)建器以及改進(jìn)的工具支持。
相關(guān)廠商內(nèi)容
活動(dòng):體驗(yàn)基于OpenSolaris的Web/企業(yè)應(yīng)用(8.30 杭州)
SOY Framework:Java富客戶端快速開發(fā)框架
相關(guān)贊助商
為什么一個(gè)更加groovy的Groovy是很重要的
Groovy的關(guān)鍵賣點(diǎn)始終是它與Java的無(wú)縫集成。你能夠很容易地把Groovy和Java的類混合搭配:你可以讓一個(gè)Java類實(shí)現(xiàn)一個(gè)Groovy接口,然后讓一個(gè)Groovy類繼承那個(gè)Java類,或者相反。不幸的是,絕大多數(shù)其他的候選的JVM語(yǔ)言不能讓你無(wú)縫的在兩種不同的語(yǔ)言之間交換類。因此,如果你希望為工作使用最好的語(yǔ)言而不放棄優(yōu)美的類的層次結(jié)構(gòu),你沒(méi)有太多的選擇,而Groovy使得你可以自由的將兩種語(yǔ)言以幾乎透明的方式集成在一起。
Groovy與Java共享同樣的庫(kù),同樣的對(duì)象模型,同樣的線程模型,同樣的安全模型。在某種意義上,你可以認(rèn)為Groovy是你的Java項(xiàng)目的一個(gè)實(shí)現(xiàn)細(xì)節(jié),而不必忍受阻抗失配問(wèn)題。
Groovy就是Java,而且Groovy使得Java更groovy了。與其他語(yǔ)言相比,Groovy對(duì)于Java開發(fā)者無(wú)疑提供了最平滑的學(xué)習(xí)曲線,這得益于兩者非常相似的語(yǔ)法。
需要牢記的是Groovy產(chǎn)生的是正常的Java字節(jié)碼而且使用普通的JDK庫(kù),所以你不需要學(xué)習(xí)全部的新的API而且不需要復(fù)雜的集成機(jī)制:極其方便,Groovy和Java是可以相互交換的。附加的好處是你可以保護(hù)對(duì)你的Java開發(fā)人員Java技巧方面的投資,或者是昂貴的應(yīng)用服務(wù)器,或者第三方的或者公司自己開發(fā)的庫(kù),你可以在Groovy中毫無(wú)問(wèn)題地重用他們。
其他不支持強(qiáng)類型的候選語(yǔ)言,在調(diào)用JDK、第三方庫(kù)或者公司自己的庫(kù)的時(shí)候,由于它們不能辨別同一方法的某一多態(tài)變種,所以始終不能調(diào)用所有的Java方法。當(dāng)你選擇一種語(yǔ)言來(lái)提高你的生產(chǎn)率或者使你的代碼可讀性更強(qiáng)的時(shí)候,如果你需要調(diào)用其他Java類,你必須非常謹(jǐn)慎的選擇語(yǔ)言,因?yàn)榭赡軙?huì)碰到很多麻煩。
今天,所有主要的企業(yè)框架都需要使用注解、枚舉或者泛型這樣的語(yǔ)言特性來(lái)充分提高它們的效率。幸運(yùn)的是,開發(fā)者使用Groovy1.5的話就可以在他們的項(xiàng)目中使用所有的Java 5特性并因此而獲益。讓我們看看在Groovy中如何使用注解,枚舉和泛型。
Java 5增加的部分
Groovy編譯器始終產(chǎn)生與以前的Java VM兼容的Java字節(jié)碼,但是由于Groovy使用了JDK1.4的核心庫(kù),所以Groovy依賴于JDK1.4。然而,對(duì)于這些Java 5中增加的部分,肯定需要使用Java 5的字節(jié)碼。例如,產(chǎn)生的類中也許包含代表著運(yùn)行時(shí)保留策略注解的字節(jié)碼信息。所以,雖然Groovy1.5能夠在JDK1.4上運(yùn)行,但是某些Groovy的特征只能在JDK1.5上使用 —— 出現(xiàn)這種情況時(shí),本文會(huì)作出聲明。
可變的參數(shù)
在Java 5中創(chuàng)建了省略號(hào)表示法,代表方法的參數(shù)是可變長(zhǎng)度的。通過(guò)三個(gè)小圓點(diǎn),Java允許用戶在一個(gè)方法的末端輸入相同類型的任意數(shù)量的參數(shù) —— 實(shí)際上,可變長(zhǎng)度參數(shù)(vararg)就是一個(gè)那種類型的元素的數(shù)組。可變長(zhǎng)度參數(shù)在Groovy 1.0中已經(jīng)出現(xiàn)了 —— 現(xiàn)在仍然可以在JDK1.4運(yùn)行時(shí)環(huán)境下工作,1.0足以向你展示如何來(lái)使用他們了。基本上,只要當(dāng)一個(gè)方法的最后一個(gè)參數(shù)是一個(gè)對(duì)象數(shù)組,或者是一個(gè)有三個(gè)點(diǎn)的參數(shù),你就可以向這個(gè)方法傳入多重參數(shù)。
第一個(gè)例子介紹了在Groovy中用省略號(hào)來(lái)使用可變長(zhǎng)度變量的方法:
int sum(int... someInts) { def total = 0 for (int i = 0; i < someInts.size(); i++) total += someInts[i] return total } assert sum(1) == 1 assert sum(1, 2) == 3 assert sum(1, 2, 3) == 6
這個(gè)例子中所用的斷言顯示了我們?nèi)绾蝹魅肴我舛嗟膇nt類型的參數(shù)。還有一個(gè)有趣的地方,為了更好的兼容Java語(yǔ)法,Java中經(jīng)典的循環(huán)方式也加入了Groovy中 —— 盡管在groovy中更有groovy特色的循環(huán)是用in關(guān)鍵字,同樣可以透明地遍歷各種各樣的數(shù)組或者集合類型。
請(qǐng)注意使用一個(gè)數(shù)組作為最后一個(gè)參數(shù)同樣可以支持可變長(zhǎng)度變量,就像下面這樣聲明方法:
int sum(int[] someInts) { /* */ }
這個(gè)代碼片斷是非常無(wú)聊的。很明顯有很多更有表現(xiàn)力的方式來(lái)計(jì)算一個(gè)總和。例如,如果你有一個(gè)數(shù)字的列表,你可以在一行代碼中計(jì)算他們的總和:
assert [1, 2, 3].sum() == 6
在Groovy中可變長(zhǎng)度變量不需要JDK 5作為基本的Java運(yùn)行時(shí)環(huán)境,在下面的章節(jié)中我們要介紹的注解則需要JDK 5。
注解
正如在JBoss Seam的文檔中所介紹的那樣,Seam支持使用Groovy來(lái)寫Seam的實(shí)體,控制器和組件,類似@Entity,@Id,@Override以及其他的注解可以用來(lái)修飾你的bean:
@Entity @Name("hotel") class Hotel implements Serializable { @Id @GeneratedValue Long id @Length(max=50) @NotNull String name @Length(max=100) @NotNull String address @Length(max=40) @NotNull String city @Length(min=2, max=10) @NotNull String state @Length(min=4, max=6) @NotNull String zip @Length(min=2, max=40) @NotNull String country @Column(precision=6, scale=2) BigDecimal price @Override String toString() { return "Hotel(${name}, ${address}, ${city}, ${zip})" } }
Hotel實(shí)體用@Entity注解來(lái)標(biāo)識(shí),用@Name給了它一個(gè)名字。可以向你的注解傳遞不同的參數(shù),例如在@Length注解約束中,為了做有效性檢查可以給注解設(shè)置不同的上界和下界。在實(shí)例中你還會(huì)注意到Groovy的屬性:getter方法和setter方法都到哪里去了?公有或者私有的修飾符在哪里?你不必等待Java 7或者Java 8來(lái)獲得屬性!在Groovy中,按照慣例,定義一個(gè)屬性非常簡(jiǎn)單:String country:這樣就會(huì)自動(dòng)生成一個(gè)私有的country成員變量,同時(shí)生成一個(gè)公有的getter和setter方法。你的代碼自然而然的變得簡(jiǎn)潔而易讀。
在Groovy中,注解可以象在Java中一樣用在類、成員變量、方法和方法參數(shù)上。但是,有兩個(gè)很容易犯錯(cuò)誤的地方需要小心。第一,你可以在Groovy中用注解,可是你不能定義它們 —— 然而,在一個(gè)快要到來(lái)的Groovy版本中將可以定義注解。第二,雖然Groovy的語(yǔ)法幾乎與Java的語(yǔ)法100%相同,但是在注解中傳入一個(gè)數(shù)組作為參數(shù)時(shí)還是有一點(diǎn)點(diǎn)不同:Groovy不是用圓括號(hào)來(lái)括起元素,而是需要使用方括號(hào),目的是為了提供更一致的語(yǔ)法 —— 在Groovy中列表和數(shù)組都用方括號(hào)來(lái)括起他們的元素。
通過(guò)Groovy1.5中的注解,你可以在Groovy中方便地為JPA或者Hibernate定義你的的帶注解的bean
(http://www.curious-creature.org/2007/03/25/persistence-made-easy-with-groovy-and-jpa/),在你的Spring服務(wù)上增加一個(gè)@Transactional 注解,使用TestNG和Fest來(lái)測(cè)試你的Swing UI(http://www.jroller.com/aalmiray/entry/testing_groovy_uis_with_fest)。在Groovy項(xiàng)目中你可以使用所有支持注解的有用而強(qiáng)大的企業(yè)框架。
枚舉
當(dāng)你需要一組固定數(shù)量的相同類型的常量時(shí),枚舉是很方便的。例如你需要一種干凈的方式來(lái)為日期定義常量而不借助使用整數(shù)常量,那么枚舉是你的好幫手。下面的片斷顯示了如何定義一星期中的日子:
enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
一旦你定義了你的枚舉,你可以在Java中以通常的記法Day.MONDAY來(lái)使用它,還可以使用枚舉來(lái)潤(rùn)色你的switch/case語(yǔ)句:
def today = Day.SATURDAY switch (today) { // Saturday or Sunday case [Day.SATURDAY, Day.SUNDAY]: println "Weekends are cool" break // a day between Monday and Friday case Day.MONDAY..Day.FRIDAY: println "Boring work day" break default: println "Are you sure this is a valid day?" }
請(qǐng)注意Groovy的switch語(yǔ)句比類似C風(fēng)格語(yǔ)言的switch語(yǔ)句要強(qiáng)大一些,在Groovy中可以在switch和case語(yǔ)句使用任何類型的對(duì)象。不用為每一個(gè)枚舉值羅列七個(gè)不同的case語(yǔ)句塊,你可以在列表或者ranges(Groovy集合類的一種類型)中重新分組case語(yǔ)句:當(dāng)值出現(xiàn)在列表或者range中,case將為真而且會(huì)執(zhí)行它關(guān)聯(lián)的命令。
受到Java教程的啟示,這里是一個(gè)更復(fù)雜的關(guān)于天文學(xué)的例子,向你展示了在枚舉中如何包含屬性,構(gòu)造器和方法:
enum Planet { MERCURY (3.303e+23, 2.4397e6), VENUS (4.869e+24, 6.0518e6), EARTH (5.976e+24, 6.37814e6), MARS (6.421e+23, 3.3972e6), JUPITER (1.9e+27, 7.1492e7), SATURN (5.688e+26, 6.0268e7), URANUS (8.686e+25, 2.5559e7), NEPTUNE (1.024e+26, 2.4746e7) double mass double radius Planet(double mass, double radius) { this.mass = mass; this.radius = radius; } void printMe() { println "${name()} has a mass of ${mass} " + "and a radius of ${radius}" } } Planet.EARTH.printMe()
與注解一樣,由于產(chǎn)生了Java 5的字節(jié)碼,Groovy中的枚舉需要JDK 5+的環(huán)境才能運(yùn)行,
靜態(tài)導(dǎo)入
在前面關(guān)于枚舉的例子中,我們始終需要在枚舉值的前面加上它的父枚舉類,但是通過(guò)靜態(tài)導(dǎo)入(可以在JDK1.4運(yùn)行時(shí)環(huán)境上工作)我們可以去掉Planet前綴,從而節(jié)省一些字符。
import static Planet.* SATURN.printMe()
這樣就不再需要Planet前綴。當(dāng)然,靜態(tài)導(dǎo)入不僅僅對(duì)枚舉有效,對(duì)其他類和靜態(tài)成員變量同樣有效。我們不妨作些數(shù)學(xué)計(jì)算。
import static java.lang.Math.* assert sin(PI / 6) + cos(PI / 3) == 1
java.lang.Math的靜態(tài)方法和靜態(tài)常量都被靜態(tài)導(dǎo)入了,這樣使得表達(dá)式更加簡(jiǎn)明。但是如果sine和cosine的縮寫不便于你閱讀,那么你可以使用Groovy中的as關(guān)鍵字來(lái)做別名:
import static java.lang.Math.PI import static java.lang.Math.sin as sine import static java.lang.Math.cos as cosine assert sine(PI / 6) + cosine(PI / 3) == 1
別名不僅僅用于靜態(tài)導(dǎo)入,也可以用于正常的導(dǎo)入,是很有用的方法。例如在很多框架中有名字非常長(zhǎng)的類,可以使用別名來(lái)增加快捷記法,或者重命名名字不太直觀的方法或者常量,或者重命名與你的命名約定標(biāo)準(zhǔn)不一致的方法或常量。
泛型
在Java 5中有爭(zhēng)議的特性:泛型,也出現(xiàn)在Groovy 1.5的最新版本中。畢竟,開始的時(shí)候可能覺(jué)得在一個(gè)動(dòng)態(tài)語(yǔ)言中加入更多類型信息是多余的。Java開發(fā)人員通常相信因?yàn)轭愋筒脸榱讼蚝蠹嫒軯ava以前的版本)使得在類的字節(jié)碼中沒(méi)有保留代表泛型的類型信息。然而,這是錯(cuò)誤的看法,通過(guò)反射API,你可以內(nèi)省一個(gè)類從而發(fā)現(xiàn)它的成員變量類型或者它的有泛型詳細(xì)信息的方法參數(shù)類型。
例如,當(dāng)你聲明了類型為List的成員變量時(shí),這個(gè)信息是在字節(jié)碼的某個(gè)地方以某種元信息的方式保存的,盡管這個(gè)成員變量確實(shí)僅僅是List類型的。這種反射信息被諸如JPA或者Hibernate這樣的企業(yè)框架所使用,將一個(gè)元素的集合中的實(shí)體關(guān)聯(lián)到代表這些元素的類型的實(shí)體。
為了實(shí)踐這些理論,讓我們檢查泛型信息是否保存在類的成員變量中。
class Talk { String title } class Speaker { String name List talks = [] } def me = new Speaker( name: 'Guillaume Laforge', talks: [ new Talk(title: 'Groovy'), new Talk(title: 'Grails') ]) def talksField = me.class.getDeclaredField('talks') assert talksField.genericType.toString() == 'java.util.Listt'
我們定義了兩個(gè)類:一個(gè)在會(huì)議上給出Talk的Speaker類。在Speaker類中,talks屬性的類型是List。然后,我們創(chuàng)建了一個(gè)Speaker實(shí)例,用兩個(gè)優(yōu)美的捷徑來(lái)初始化name和talks屬性,并創(chuàng)建了一個(gè)Talk實(shí)例的列表。當(dāng)初始化代碼就緒后,我們?nèi)〉么韙alks的成員變量,然后檢查泛型信息是否正確:正確!talks是一個(gè)List,但是它是一個(gè)Talk的List。
共變的返回類型
在Java 5中,如果你在一個(gè)子類中有一個(gè)方法,其名稱與參數(shù)類型與父類中的方法相同,但是返回值是父類方法的返回值的子類,那么我們可以覆蓋父類的方法。在Groovy1.0中,不支持共變的返回類型。但是在Groovy1.5中,你可以使用共變返回類型。而且,如果你試圖覆蓋一個(gè)方法而返回類型不是父類方法的返回類型的子類,將拋出一個(gè)編譯錯(cuò)誤。共變的返回類型對(duì)于參數(shù)化的類型同樣有效。
除了因?yàn)橹С諮ava 5的特性而給Groovy語(yǔ)言帶來(lái)了一些增強(qiáng)外,Groovy1.5還引入了其他一些語(yǔ)法的增強(qiáng),我們?cè)谙旅娴恼鹿?jié)中會(huì)探索這些部分。
增加的語(yǔ)法
Elvis操作符
Java 5的特性除了帶給Groovy注解,泛型和枚舉,還增加了一個(gè)新操作符—— ?:,Elivis操作符。當(dāng)你看這個(gè)操作符的時(shí)候,你很容易猜測(cè)為什么會(huì)這樣命名 —— 如果不是,可以根據(jù)Smiley來(lái)思考。這個(gè)新操作符實(shí)際上是一個(gè)三目操作符的便捷記法。你是否經(jīng)常使用三目操作符來(lái)改變一個(gè)變量的值?如果它是null那么給它分配一個(gè)缺省值。在Java中典型的情況是這樣的:
String name = "Guillaume"; String displayName = name != null ? name : "Unknown";
在Groovy中,由于語(yǔ)言本身可以按需“強(qiáng)制”類型轉(zhuǎn)換到布爾值(例如在if或者while構(gòu)造中條件表達(dá)式需要為布爾值),在這個(gè)語(yǔ)句中,我們可以忽略和null的比較,因?yàn)楫?dāng)一個(gè)String是null的時(shí)候,它被強(qiáng)制轉(zhuǎn)換為false,所以在Groovy中語(yǔ)句會(huì)變?yōu)椋?/p>
String name = "Guillaume" String displayName = name ? name : "Unknown"
然而,你仍然會(huì)注意到name變量的重復(fù),這破壞了DRY原則(不要重復(fù)你自己 Don't Repeat Yourself)。由于這個(gè)構(gòu)造非常普遍,所以引入了Elvis操作符來(lái)簡(jiǎn)化這些重復(fù)的現(xiàn)象,語(yǔ)句變成:
String name = "Guillaume" String displayName = name ?: "Unknown"
name變量的第二次出現(xiàn)被簡(jiǎn)單的忽略了,三目操作符不再是三目的了,縮短為這種更簡(jiǎn)明的形式。
還有一點(diǎn)值得注意的是這個(gè)新的構(gòu)造沒(méi)有副作用,由于第一個(gè)元素(這里是name)不會(huì)象在三目操作符中那樣被估值兩次,所以不需要引入一個(gè)中間的臨時(shí)變量來(lái)保持三目操作符中第一個(gè)元素的第一次估值。
經(jīng)典的循環(huán)
雖然Groovy嚴(yán)格地來(lái)說(shuō)不是100%的Java的超集,但是在每一個(gè)Groovy的新版本中,其語(yǔ)法都更接近Java的語(yǔ)法,在Groovy中越來(lái)越多的Java代碼是有效的。這種兼容性的好處是當(dāng)你開始用Groovy工作時(shí),你可以拷貝并粘貼Java代碼到你的Groovy類中,它們會(huì)如你所愿地工作。然后,隨著時(shí)間的推移你學(xué)習(xí)了Groovy語(yǔ)言,你可以扔掉那些從Java拷貝來(lái)的在Groovy中不地道的代碼,使用GStrings(內(nèi)插字符串),或者閉包等等。Groovy為Java開發(fā)者提供了一個(gè)非常平滑的學(xué)習(xí)曲線。
然而,Groovy中有一處忽略了Java語(yǔ)法的兼容性,實(shí)際上Groovy中不允許使用從Java語(yǔ)言的C背景繼承而來(lái)的經(jīng)典的循環(huán)語(yǔ)法。最初,Groovy開發(fā)者認(rèn)為經(jīng)典的循環(huán)語(yǔ)法不是最好的,他們更喜歡使用可讀性更好的for/in構(gòu)造。但是由于Groovy用戶經(jīng)常要求Groovy包含這個(gè)舊的循環(huán)構(gòu)造,所以Groovy團(tuán)隊(duì)決定支持它。
在Groovy 1.5中,你可以選擇Groovy的for/in構(gòu)造,或者經(jīng)典的for循環(huán)構(gòu)造:
for (i in 0..9) println i for (int i = 0; i < 10; i++) println i
最終,這也許只是品味不同,Groovy的熟手用戶通常更喜歡for/in循環(huán)這樣更加簡(jiǎn)明的語(yǔ)法。
沒(méi)有圓括號(hào)的命名參數(shù)
由于易適應(yīng)且簡(jiǎn)明的語(yǔ)法,以及高級(jí)的動(dòng)態(tài)能力,Groovy是實(shí)現(xiàn)內(nèi)部領(lǐng)域特定語(yǔ)言(Domain-Specific Languages)的理想選擇。當(dāng)你希望在業(yè)務(wù)問(wèn)題專家和開發(fā)者之間共享一種公共的比喻說(shuō)法的時(shí)候,你可以借Groovy之力來(lái)創(chuàng)建一個(gè)專用的商業(yè)語(yǔ)言,用該語(yǔ)言為你的應(yīng)用的關(guān)鍵概念和商業(yè)規(guī)則建模。這些DSL的一個(gè)重要方面是使得代碼非常可讀,而且讓非技術(shù)人員更容易寫代碼。為了更進(jìn)一步實(shí)現(xiàn)這個(gè)目標(biāo),Groovy的語(yǔ)法做了通融,允許我們使用沒(méi)有圓括號(hào)括起來(lái)的命名參數(shù)。
首先,在Groovy中命名參數(shù)看起來(lái)是這樣的:
fund.compare(to: benchmarkFund, in: euros) compare(fund: someFund, to: benchmark, in: euros)
通過(guò)向數(shù)字加入新的屬性 —— 這在Groovy中是可能的,但是超出了這篇文章的范圍 —— 我們可以寫出像這樣的代碼:
monster.move(left: 3.meters, at: 5.mph)
現(xiàn)在通過(guò)忽略圓括號(hào),代碼變得更清晰了:
fund.compare to: benchmarkFund, in: euros compare fund: someFund, to: benchmark, in: euros monster.move left: 3.meters, at: 5.mph
顯然,這沒(méi)有很大的區(qū)別,但是每個(gè)語(yǔ)句變得更接近淺白的英語(yǔ)句子,而且在宿主語(yǔ)言中刪除了通常冗余的技術(shù)代碼。Groovy語(yǔ)言這個(gè)小小的增強(qiáng)給予了商業(yè)DSL設(shè)計(jì)人員更多的選擇。
改善的工具支持
當(dāng)Groovy還不成熟的時(shí)候,一個(gè)常見(jiàn)的弱點(diǎn)是缺乏好的工具支持:工具系列和IDE支持都不到位。幸運(yùn)的是,隨著Groovy和Grails web框架的成熟和成功,這種狀況得到了改變。
“聯(lián)合”編譯器的介紹
Groovy以它與Java的透明而且無(wú)縫的集成而聞名。但是這不僅僅意味著在Groovy腳本中可以調(diào)用Java方法,不,兩個(gè)語(yǔ)言之間的集成遠(yuǎn)不止于此。例如,一個(gè)Groovy類繼承一個(gè)Java類,而該Java類實(shí)現(xiàn)一個(gè)Groovy接口是完全可能的,反之亦然。不幸的是,其他候選語(yǔ)言不支持這樣做。然而,到目前為止,當(dāng)把Groovy和Java混合起來(lái)使用的時(shí)候,你在編譯時(shí)要小心選擇正確的編譯順序,如果兩個(gè)語(yǔ)言中出現(xiàn)循環(huán)依賴,那么你也許會(huì)碰到一個(gè)“雞與蛋”的問(wèn)題。幸運(yùn)的是在Groovy 1.5中這不再是問(wèn)題,謝謝獲獎(jiǎng)的Java IDE IntelliJ IDEA的創(chuàng)建者JetBrains的一個(gè)貢獻(xiàn),你可以使用一個(gè)“聯(lián)合”編譯器將Groovy和Java代碼放在一起一次編譯而不必考慮類之間的依賴關(guān)系。
如果你希望在命令行使用聯(lián)合編譯器,你可以像通常那樣調(diào)用groovyc命令,但是使用-j參數(shù)來(lái)進(jìn)行聯(lián)合編譯:
groovyc *.groovy *.java -j -Jsource=1.4 -Jtarget=1.4
為了向基本的javac命令傳遞參數(shù),你可以用J作為參數(shù)的前綴。你還可以在你的Ant或者M(jìn)aven構(gòu)建文件中使用聯(lián)合編譯器執(zhí)行Ant任務(wù):
<taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="my.classpath"/> <groovyc srcdir="${mainSourceDirectory}" destdir="${mainClassesDirectory}" classpathref="my.classpath" jointCompilationOptions="-j -Jsource=1.4 -Jtarget=1.4" />
Groovy的Maven插件
對(duì)于Maven用戶,在Codehaus有一個(gè)全特性的Maven插件項(xiàng)目允許你構(gòu)建自己的Java/Groovy應(yīng)用:編譯你的Groovy和Java代碼,從JavaDoc標(biāo)簽生成文檔,甚至允許你在Groovy中開發(fā)自己的Maven插件。還有一個(gè)Maven的原型可以更迅速的引導(dǎo)你的Groovy項(xiàng)目。要得到更多信息,你可以參考插件的文檔:http://mojo.codehaus.org/groovy/index.html
GroovyDoc文檔工具
作為一個(gè)Java開發(fā)人員,你習(xí)慣于通過(guò)你的類,接口,成員變量或者方法的注釋中的JavaDoc標(biāo)簽來(lái)生成代碼文檔。在Groovy中,你仍然可以在你的注釋中使用這樣的標(biāo)簽,使用一個(gè)叫做GroovyDoc的工具為你所有的Groovy類生成與JavaDoc同樣的文檔。
這里有一個(gè)Ant任務(wù),你可以定義并用它來(lái)產(chǎn)生文檔:
<taskdef name="groovydoc" classname="org.codehaus.groovy.ant.Groovydoc"> <classpath> <path path="${mainClassesDirectory}"/> <path refid="compilePath"/> </classpath> </taskdef> <groovydoc destdir="${docsDirectory}/gapi" sourcepath="${mainSourceDirectory}" packagenames="**.*" use="true" windowtitle="Groovydoc" private="false"/>
新的交互性shell和Swing控制臺(tái)
Groovy的發(fā)行版本總是包含兩個(gè)不同的shell:一個(gè)命令行shell和一個(gè)Swing控制臺(tái)。命令行shell,Groovysh,就其與用戶的交互性而言從來(lái)都不是很友好:當(dāng)你希望執(zhí)行一個(gè)語(yǔ)句的時(shí)候,你不得不在每個(gè)語(yǔ)句后面鍵入“go”或者“execute”,這樣才能執(zhí)行。為了某些快速的原型開發(fā)或者試用一些新的API,每次都鍵入“go”是非常累贅的。在Groovy 1.5中情況變化了,有了新的交互式的shell。不再需要鍵入“go”。
這個(gè)新的shell有幾個(gè)增強(qiáng)的特性,例如使用了提供ANSI著色的JLine庫(kù),tab命令補(bǔ)全,行編輯能力。你可以與不同的腳本緩沖器工作,記住已經(jīng)導(dǎo)入的類,裝載現(xiàn)存的腳本,將當(dāng)前腳本保存到一個(gè)文件中,瀏覽歷史記錄,等等。欲得到shell所支持特性的更詳細(xì)解釋,請(qǐng)參閱文檔。
不僅僅命令行shell得到了提高,Swing控制臺(tái)也有改進(jìn),有了新的工具條,先進(jìn)的undo能力,可以增大或者縮小字體,語(yǔ)法高亮等,總之,控制臺(tái)有了很多提高。
IntelliJ IDEA JetGroovy 插件
JetGroovy插件是最棒的工具支持:一個(gè)免費(fèi)而且開源的專用于支持Groovy和Grails的IntelliJ IDEA插件。這個(gè)插件是由JetBrains他們自己開發(fā)的,對(duì)于語(yǔ)言和Web框架都提供了無(wú)以倫比的支持。
插件對(duì)Groovy有專門的支持,其中部分特性:
- 對(duì)于所有的語(yǔ)法都可以語(yǔ)法高亮,對(duì)于未識(shí)別的類型加不同的警告。
- 可以運(yùn)行Groovy類,腳本和用Groovy寫的JUnit測(cè)試用例。
- 調(diào)試器:你可以一步一步地運(yùn)行你的Java和Groovy代碼,設(shè)置斷點(diǎn),顯示變量,當(dāng)前的堆棧信息等等。
- 聯(lián)合編譯器:編譯器將Groovy和Java一起編譯,可以解決語(yǔ)言之間的依賴問(wèn)題。
- 代碼補(bǔ)全,可以補(bǔ)全包,類,屬性,成員變量,變量,方法,關(guān)鍵字,甚至對(duì)于Swing UI builder有特殊的支持。
- 先進(jìn)的類搜索和發(fā)現(xiàn)功能。
- 重構(gòu):大多數(shù)在Java中你所喜愛(ài)的常用重構(gòu)功能都可以在Java和Groovy中使用,例如“surround with”,介紹、內(nèi)聯(lián)或者重命名一個(gè)變量,重命名包、類、方法和成員變量。
- 導(dǎo)入優(yōu)化和代碼格式化。
- 結(jié)構(gòu)視圖:對(duì)你的類有一個(gè)鳥瞰視圖。
最終,考慮到在IntelliJ IDEA中提供的支持和相互影響的程度,你甚至不會(huì)意識(shí)到你是在Groovy中還是在Java中開發(fā)一個(gè)類。如果你正在考慮在你的Java項(xiàng)目中增加一些Groovy或者你打算開發(fā)Grails應(yīng)用,這個(gè)插件是肯定要安裝的。
你可以在JetBrains站點(diǎn)得到更多信息。
盡管我僅僅表?yè)P(yáng)了IntelliJ IDEA的Groovy插件,但是你不必因此改變你的Groovy開發(fā)習(xí)慣。你可以使用由IBM的Zero項(xiàng)目開發(fā)者持續(xù)改進(jìn)的Eclipse插件,或者Sun的NetBeans的Groovy和Grails插件。
性能提高
Groovy的新版本除了增加新特性,與以前的版本相比還顯著地提高了性能,并且降低了內(nèi)存消耗。在我們的非正式的基準(zhǔn)測(cè)試中,我們發(fā)現(xiàn)與Groovy 1.5 beta版相比我們所有測(cè)試套件的運(yùn)行速度有了15%到45%的提高 —— 與Groovy 1.0相比肯定有更多的提高。雖然還需要開發(fā)更正式的基準(zhǔn)測(cè)試,但是一些開發(fā)人員已經(jīng)證實(shí)了這些測(cè)試數(shù)字,一家保險(xiǎn)公司的開發(fā)人員正在使用Groovy來(lái)寫他們的策略風(fēng)險(xiǎn)計(jì)算引擎的商業(yè)規(guī)則,另一個(gè)公司在高并發(fā)機(jī)器上運(yùn)行了多個(gè)測(cè)試。總的來(lái)說(shuō),Groovy在絕大多數(shù)情況下會(huì)更快,更輕盈。不過(guò)在具體的應(yīng)用中,效果還要看你如何使用Groovy。
增強(qiáng)的動(dòng)態(tài)能力
由于Groovy和Grails項(xiàng)目的共生關(guān)系,Grails核心部分中成熟的動(dòng)態(tài)能力已經(jīng)被引入到Groovy中。
Groovy是一個(gè)動(dòng)態(tài)語(yǔ)言:簡(jiǎn)單的說(shuō),這意味著某些事情,例如方法分派發(fā)生在運(yùn)行時(shí),而不是象Java和其他語(yǔ)言那樣發(fā)生在編譯時(shí)。在Groovy中有一個(gè)特殊的運(yùn)行時(shí)系統(tǒng),叫做MOP(元對(duì)象協(xié)議Meta-Object Protocol),負(fù)責(zé)方法分派邏輯。幸運(yùn)的是,這個(gè)運(yùn)行時(shí)系統(tǒng)非常開放,人們可以深入系統(tǒng)并且改變系統(tǒng)的通常行為。對(duì)于每一個(gè)Java類和每一個(gè)Groovy實(shí)例,都有一個(gè)與之相關(guān)聯(lián)的元類(meta-class)代表該對(duì)象的運(yùn)行時(shí)行為。Groovy為你與MOP交互提供了幾種不同的方法,可以定制元類,可以繼承某些基類,但是謝謝Grails項(xiàng)目的貢獻(xiàn),有一種更groovy的元類:expando元類。
代碼例子可以幫助我們更容易地理解概念。在下面的例子中,字符串msg的實(shí)例有一個(gè)元類,我們可以通過(guò)metaClass屬性訪問(wèn)該元類。然后我們改變String類的元類,為其增加一個(gè)新方法,為toUpperCase()方法提供一個(gè)速記記法。之后,我們?yōu)樵惖膗p屬性分配一個(gè)閉包,這個(gè)屬性是在我們把閉包分配給它的時(shí)候創(chuàng)建的。這個(gè)閉包沒(méi)有參數(shù)(因此它以一個(gè)箭頭開始),我們?cè)陂]包的委托之上調(diào)用toUpperCase()方法,這個(gè)委托是一個(gè)特殊的閉包變量,代表著真實(shí)的對(duì)象(這里是String實(shí)例)。
def msg = "Hello!" println msg.metaClass String.metaClass.up = { -> delegate.toUpperCase() } assert "HELLO!" == msg.up()
通過(guò)這個(gè)元類,你可以查詢對(duì)象有哪些方法或者屬性:
// print all the methods obj.metaClass.methods.each { println it.name } // print all the properties obj.metaClass.properties.each { println it.name }
你甚至可以檢查某個(gè)特定的方法或者屬性是否可用,比使用instanceof來(lái)檢查的粒度要小的多:
def msg = 'Hello!' if (msg.metaClass.respondsTo(msg, 'toUpperCase')) { println msg.toUpperCase() } if (msg.metaClass.hasProperty(msg, 'bytes')) { println foo.bytes.encodeBase64() }
這些機(jī)制在Grails web框架中得到了廣泛的使用,例如創(chuàng)建一個(gè)動(dòng)態(tài)查找器:由于你可以在一個(gè)Book領(lǐng)域類上調(diào)用一個(gè)findByTitle()動(dòng)態(tài)方法,所以在大多數(shù)情況下不需要DAO類。通過(guò)元類,Grails自動(dòng)為領(lǐng)域類加入了這樣的方法。此外,如果被調(diào)用的方法不存在,在第一次調(diào)用的時(shí)候方法會(huì)被創(chuàng)建并緩存。這可以由下面解釋的其他高級(jí)技巧來(lái)完成。
除了我們已經(jīng)看到的例子,expando元類也提供了一些補(bǔ)充的功能。在一個(gè)expando元類中可以加入四個(gè)其他方法:
- invokeMethod() 讓你可以攔截所有的方法調(diào)用,
- 而methodMissing() 僅僅在沒(méi)有發(fā)現(xiàn)其他方法的時(shí)候被調(diào)用。
- get/setProperty() 攔截對(duì)所有屬性的訪問(wèn),
- 而propertyMissing()在沒(méi)有發(fā)現(xiàn)屬性的時(shí)候被調(diào)用。
與以前的Groovy版本相比,通過(guò)expando元類可以更容易定制你的應(yīng)用行為,并且節(jié)約昂貴的開發(fā)時(shí)間。很明顯,不是每個(gè)人都需要使用這些技術(shù),但是在許多場(chǎng)合這些技術(shù)是很方便的,例如你想應(yīng)用某些AOP(面向方面的編程Aspect Oriented Techniques)來(lái)裝飾你的類,或者想通過(guò)刪除某些不必要的冗余代碼來(lái)簡(jiǎn)化你的應(yīng)用的商業(yè)邏輯代碼并使其可讀性更強(qiáng)。
Steroids之上的Swing
Groovy項(xiàng)目有一個(gè)天才的Swing開發(fā)者團(tuán)隊(duì),他們努力工作使得在Groovy中用Swing來(lái)構(gòu)建用戶界面的能力更強(qiáng)大。在Groovy中構(gòu)建Swing UI的基石是SwingBuilder類:在你的代碼中,你可以在語(yǔ)法級(jí)別可視化的看到Swing組件是如何彼此嵌套的。Groovy web站點(diǎn)的一個(gè)過(guò)分簡(jiǎn)單的例子顯示了如何簡(jiǎn)單地創(chuàng)建一個(gè)小的GUI程序:
import groovy.swing.SwingBuilder import java.awt.BorderLayout import groovy.swing.SwingBuilder import java.awt.BorderLayout as BL def swing = new SwingBuilder() count = 0 def textlabel def frame = swing.frame(title:'Frame', size:[300,300]) { borderLayout() textlabel = label(text:"Clicked ${count} time(s).", constraints: BL.NORTH) button(text:'Click Me', actionPerformed: {count++; textlabel.text = "Clicked ${count} time(s)."; println "clicked"}, constraints:BorderLayout.SOUTH) } frame.pack() frame.show()
Swing構(gòu)建器的概念已經(jīng)擴(kuò)展到提供定制的組件工廠。有一些不是缺省包含在Groovy中的附加模塊,它們把JIDE或者SwingX項(xiàng)目中的Swing組件集成到Swing構(gòu)建器代碼中。
在這個(gè)版本中,界面部分有很多改進(jìn),值得用一整篇文章來(lái)敘述。我僅僅列出其中一部分,例如bind()方法。受到JSR (JSR-295)的bean綁定(beans binding)的啟發(fā),你可以很容易地將組件或者bean綁定到一起,使得它們?cè)趯?duì)方發(fā)生變化的時(shí)候作出反應(yīng)。在下面的例子中,按鈕的間隔尺寸會(huì)根據(jù)滾動(dòng)條組件的值的變化而變化。
import groovy.swing.SwingBuilder import java.awt.Insets swing = new SwingBuilder() frame = swing.frame { vbox { slider(id: 'slider', value:5) button('Big Button?!', margin: bind(source: slider, sourceProperty:'value', converter: { [it, it, it, it] as Insets })) } } frame.pack() frame.size = [frame.width + 200, frame.height + 200] frame.show()
在構(gòu)建用戶界面的時(shí)候?qū)⒔M件綁定在一起是非常常見(jiàn)的任務(wù),所以這個(gè)任務(wù)通過(guò)綁定機(jī)制被簡(jiǎn)化了。還可以使用其他的自動(dòng)綁定方法,但是需要一篇專門的文章來(lái)闡述。
在其他新的值得注意的特性中,新增了一些方便的方法,使得閉包可以調(diào)用聲名狼籍的SwingUtilities類,啟動(dòng)新的線程:edt()將調(diào)用invokeAndWait()方法, doLater()將會(huì)調(diào)用invokeLater()方法,doOutside()方法會(huì)在一個(gè)新線程中啟動(dòng)一個(gè)閉包。不再有丑陋的匿名內(nèi)部類:只要通過(guò)這些便捷方法使用閉包就可以!
最后但是也最重要的是,由于SwingBuilder的build()方法,分離視圖的描述與它相關(guān)聯(lián)的行為邏輯變成再簡(jiǎn)單不過(guò)的事情了。你可以創(chuàng)建一個(gè)僅僅包含視圖的單獨(dú)的腳本,而與組件的交互或者綁定都在主類中,在MVC模式中可以更清晰的分離視圖與邏輯部分。
總結(jié)
這篇文章列出了Groovy 1.5中引人注目的新特性,但是我們僅僅觸及了Groovy這個(gè)新版本的皮毛。重要的亮點(diǎn)主要圍繞著Java 5的新特性,例如注解、枚舉或者泛型:這使得Groovy可以完美地與諸如Spring、Hibernate或者JPA這樣的企業(yè)框架優(yōu)美而無(wú)縫的集成。得益于改進(jìn)的語(yǔ)法以及增強(qiáng)的動(dòng)態(tài)能力,Groovy讓你能夠創(chuàng)建內(nèi)嵌的領(lǐng)域特定語(yǔ)言來(lái)定制你的商業(yè)邏輯,并在應(yīng)用的擴(kuò)展點(diǎn)將其方便地集成進(jìn)來(lái)。由于工具支持的大幅改善,開發(fā)者的體驗(yàn)有了顯著的提高,開發(fā)體驗(yàn)不再是采用Groovy的一個(gè)障礙。總的來(lái)說(shuō),Groovy 1.5前所未有的滿足了簡(jiǎn)化開發(fā)者生活的目標(biāo),Groovy應(yīng)該成為所有Java開發(fā)者工具箱的一部分。
關(guān)于作者
Guillaume Laforge是Groovy的項(xiàng)目經(jīng)理和JSR-241規(guī)范的領(lǐng)導(dǎo)者,Java規(guī)范請(qǐng)求(Java Specification Request)在Java社區(qū)過(guò)程(Java Community Process)中標(biāo)準(zhǔn)化了Groovy語(yǔ)言。他還是Technology的副主席以及G2One, Inc.的核心創(chuàng)建者,該公司資助并領(lǐng)導(dǎo)著Groovy和Grails項(xiàng)目的發(fā)展。Guillaume經(jīng)常在不同的會(huì)議談?wù)?strong style="color: black; background-color: #ffff66">Groovy和Grails,例如JavaOne,JavaPolis,Sun TechDays,Spring Experience,Grails eXchange。
查看英文原文:What's New in Groovy 1.5posted on 2008-08-31 21:00 大石頭 閱讀(979) 評(píng)論(0) 編輯 收藏 所屬分類: Groovy