keep moving!

          We must not cease from exploration. And the end of all our exploring will be to arrive where we began and to know the place for the first time.
          隨筆 - 37, 文章 - 2, 評(píng)論 - 3, 引用 - 0
          數(shù)據(jù)加載中……

          [轉(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)容

          免費(fèi)迷你書下載:深入淺出Struts 2

          活動(dòng):體驗(yàn)基于OpenSolaris的Web/企業(yè)應(yīng)用(8.30 杭州)

          SOY Framework:Java富客戶端快速開發(fā)框架

          Hadoop中的集群配置和使用技巧

          免費(fèi)迷你書下載:Grails入門指南

          相關(guān)贊助商

          InfoQ中文站Java社區(qū),關(guān)注企業(yè)Java社區(qū)的變化與創(chuàng)新,通過(guò)新聞、文章、視頻訪談和演講以及迷你書等為中國(guó)Java技術(shù)社區(qū)提供一流資訊。

          為什么一個(gè)更加groovyGroovy是很重要的

          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è)TalkList

          共變的返回類型

          在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.5

          posted on 2008-08-31 21:00 大石頭 閱讀(979) 評(píng)論(0)  編輯  收藏 所屬分類: Groovy


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 偏关县| 乡宁县| 邵阳县| 塔城市| 北辰区| 于都县| 松溪县| 黑水县| 葵青区| 孙吴县| 颍上县| 金湖县| 依安县| 寻乌县| 平邑县| 成武县| 宣城市| 二连浩特市| 江华| 沙湾县| 江川县| 阳东县| 孟州市| 金湖县| 岳普湖县| 巴青县| 河源市| 武义县| 大庆市| 江北区| 诏安县| 大名县| 即墨市| 个旧市| 孟村| 龙江县| 开化县| 宁德市| 佛冈县| 信宜市| 都匀市|