Groovy入門教程 [email protected] 2009-5-13 一,、groovy是什么 簡單地說,,Groovy 是下一代的java語言,跟java一樣,它也運行在 JVM 中,。 作為跑在JVM中的另一種語言,,groovy語法與 Java 語言的語法很相似。同時,,Groovy 拋棄了java煩瑣的文法,。同樣的語句,使用groovy能在最大限度上減少你的擊鍵次數(shù)——這確實是“懶惰程序員們”的福音,。
二,、開發(fā)環(huán)境 1,、 jdk 1.5以上 2,、 eclipse+groovy plugin(支持Groovy 1.5.7) 打開eclipse,通過Software Updates > Find and Install...菜單,,使用“Search for new features to install” 下載并安裝groovy插件,。New一個遠(yuǎn)程站點,url可使用http://dist./groovy/distributions/update/,,插件名:Groovy plug-in,。根據(jù)需要你可以同時選擇groovy和grails(后續(xù)會學(xué)習(xí)到):
三、創(chuàng)建groovy項目 1,、 新建一個groovy項目 New --> Project à Java Project 創(chuàng)建一個java項目,。為了方便管理,建議在source中建兩個source文件夾java和groovy,分別用于存儲java源文件和groovy源文件:
2,、 添加 Groovy 特性 在項目上右擊,,Groovy à Add Groovy Nature,這樣會在項目中添加 Groovy Libraries,。
3,、 添加 Groovy 類 在項目groovy源文件下右鍵,New à Other àGroovy à Groovy Class
自動生成的源代碼如下: public class HelloWorld{ /** * @param args */ public static void main(def args){ // TODO Auto-generated method stub } } 我們在main方法中加一句打印語句: println "Hello World" 4,、 編譯運行groovy類 在源文件上右鍵,,Compile Groovy File,然后右鍵,,Run As à Groovy ,,在控制臺中查看運行結(jié)果。 實際上 groovy 語法的簡練還體現(xiàn)在,,就算整個文件中只有println "Hello World"這一句代碼(把除這一句以外的語句刪除掉吧),,程序也照樣能夠運行。 當(dāng)然,,為了說明groovy 其實就是java,,你也可以完全按照java 語法來編寫HelloWorld類。 四,、Groovy語法簡介 1,、 沒有類型的java 作為動態(tài)語言,groovy中所有的變量都是對象(類似于.net framework,,所有對象繼承自java.lang.Object),,在聲明一個變量時,groovy不要求強制類型聲明,,僅僅要求變量名前使用關(guān)鍵字def(從groovy jsr 1開始,,在以前的版本中,甚至連def都不需要),。 修改main 方法中的代碼: def var="hello world" println var println var.class 你可以看到程序最后輸出了var的實際類型為:java.lang.String 作為例外,,方法參數(shù)和循環(huán)變量的聲明不需要def。 2,、 不需要的public 你可以把main方法前面的public去掉,,實際上,groovy中默認(rèn)的修飾符就是public,,所以public修飾符你根本就不需要寫,,這點跟java不一樣。 3,、 不需要的語句結(jié)束符 Groovy中沒有語句結(jié)束符,,當(dāng)然為了與java保持一致性,,你也可以使用;號作為語句結(jié)束符。在前面的每一句代碼后面加上;號結(jié)束,,程序同樣正常運行(為了接受java程序員的頑固習(xí)慣),。 4、 字符串連接符 跟java一樣,,如果你需要把一個字符串寫在多行里,,可以使用+號連接字符串。代碼可以這樣寫: def var="hello "+ "world"+ ",groovy!" 當(dāng)然更groovy的寫法是: def var="""hello world groovy!""" 三個”號之間不在需要+號進行連接(不過字符串中的格式符都會被保留,,包括回車和tab),。 5、 一切皆對象 聽起來象是“眾生平等”的味道,,事實上groovy對于對象是什么類型并不關(guān)心,,一個變量的類型在運行中隨時可以改變,一切根據(jù)需要而定,。如果你賦給它boolean ,,那么不管它原來是什么類型,它接受boolean值之后就會自動把類型轉(zhuǎn)變?yōu)?/span>boolean值,??聪旅娴拇a: def var="hello "+ "world"+ ",groovy!" println var; println var.class; var=1001 println var.class 輸出結(jié)果: hello world,groovy! class java.lang.String class java.lang.Integer
var這個變量在程序運行中,類型在改變,。一開始給它賦值String,,它的類型就是String,后面給它賦值Integer,,它又轉(zhuǎn)變?yōu)?/span>Integer,。 6、 循環(huán) 刪除整個源文件內(nèi)容,,用以下代碼替代: def var="hello "+ "world"+ ",groovy!" def repeat(val){ for(i = 0; i < 5; i++){ println val } } repeat(var) 輸出: hello world,groovy! hello world,groovy! hello world,groovy! hello world,groovy! hello world,groovy! 注意循環(huán)變量i前面沒有def,。當(dāng)然也沒有java中常見的int,但如果你非要加上int也不會有錯,,因為從Groovy1.1beta2之后開始(不包括1.1beta2),,groovy開始支持java經(jīng)典的for循環(huán)寫法。 此外,,上面的for語句還可以寫成: for(i in 0..5) 這樣的結(jié)果是一樣的,。 7,、 String 和 Gstring 除了標(biāo)準(zhǔn)的java.lang.String以外(用’號括?。?/span>groovy還支持Gstring字符串類型(用“號括?。?。把上面的for循環(huán)中的語句改成: println "This is ${i}:${val}" 運行一下,,你就會明白什么是Gstring。 8,、 范圍 這個跟pascal中的“子界”是一樣的,。在前面的for循環(huán)介紹中我們已經(jīng)使用過的for(i in 0..5)這樣的用法,其中的0..5就是一個范圍,。 范圍 是一系列的值,。例如 “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” 包括小于 e 的所有值,。 范圍主要在for循環(huán)中使用,。 9、 默認(rèn)參數(shù)值 可以為方法指定默認(rèn)參數(shù)值,。我們修改repeat方法的定義: def repeat(val,repeat=3){ for(i in 0..<repeat){ println "This is ${i}:${val}" } } 可以看到,,repeat方法增加了一個參數(shù)repeat(并且給了一個默認(rèn)值3),用于指定循環(huán)次數(shù),。 當(dāng)我們不指定第2個參數(shù)調(diào)用repeat方法時,,repeat參數(shù)取默認(rèn)值3。 10,、 集合 Groovy支持最常見的兩個java集合:java.util.Collection和java.util.Map,。前面所說的范圍實際也是集合的一種(java.util.List)。 (1) Collection Groovy 中這樣來定義一個Collection: def collect=["a","b","c"] 除了聲明時往集合中添加元素外,,還可以用以下方式向集合中添加元素: collect.add(1); collect<<"come on"; collect[collect.size()]=100.0 Collection使用類似數(shù)組下標(biāo)的方式進行檢索: println collect[collect.size()-1] println collect[5] groovy支持負(fù)索引: println collect[-1] //索引其倒數(shù)第1個元素 println collect[-2] //索引其倒數(shù)第2個元素 Collection支持集合運算: collect=collect+5 //在集合中添加元素5 println collect[collect.size()-1] collect=collect-'a' //在集合中減去元素a(第1個) println collect[0] //現(xiàn)在第1個元素變成b了 同樣地,,你可以往集合中添加另一個集合或刪除一個集合: collect=collect-collect[0..4] //把集合中的前5個元素去掉 println collect[0] //現(xiàn)在集合中僅有一個元素,即原來的最后一個元素 println collect[-1] //也可以用負(fù)索引,,證明最后一個元素就是第一個元素 (2) Map Map是“鍵-值”對的集合,,在groovy中,,鍵不一定是String,可以是任何對象(實際上Groovy中的Map就是java.util. 如此可以定義一個Map: def map=['name':'john','age':14,'sex':'boy'] 添加項: map=map+['weight':25] //添加john的體重 map.put('length',1.27) //添加john的身高 map.father='Keller' //添加john的父親 可以用兩種方式檢索值: println map['father'] //通過key作為下標(biāo)索引 println map.length //通過key作為成員名索引 11,、 閉包(Closure) 閉包是用{符號括起來的代碼塊,它可以被單獨運行或調(diào)用,,也可以被命名,。類似‘匿名類’或內(nèi)聯(lián)函數(shù)的概念。 閉包中最常見的應(yīng)用是對集合進行迭代,,下面定義了3個閉包對map進行了迭代: map.each({key,value-> //key,value兩個參數(shù)用于接受每個元素的鍵/值 println "$key:$value"}) map.each{println it} //it是一個關(guān)鍵字,,代表map集合的每個元素 map.each({ println it.getKey()+"-->"+it.getValue()}) 除了用于迭代之外,閉包也可以單獨定義: def say={word-> println "Hi,$word!" } 調(diào)用: say('groovy') say.call('groovy&grails') 輸出: Hi,groovy! Hi,groovy&grails!
看起來,,閉包類似于方法,,需要定義參數(shù)和要執(zhí)行的語句,它也可以通過名稱被調(diào)用,。然而閉包對象(不要奇怪,,閉包也是對象)可以作為參數(shù)傳遞(比如前面的閉包作為參數(shù)傳遞給了map的each方法)。而在java中,,要做到這一點并不容易(也許C++中的函數(shù)指針可以,,但不要忘記java中沒有指針)。其次,,閉包也可以不命名(當(dāng)然作為代價,,只能在定義閉包時執(zhí)行一次),而方法不可以,。 12,、 類 Groovy類和java類一樣,你完全可以用標(biāo)準(zhǔn)java bean的語法定義一個groovy 類,。但作為另一種語言,,我們可以使用更groovy的方式定義和使用類,這樣的好處是,,你可以少寫一半以上的javabean代碼: (1) 不需要public修飾符 如前面所言,,groovy的默認(rèn)訪問修飾符就是public,如果你的groovy類成員需要public修飾,,則你根本不用寫它,。 (2) 不需要類型說明 同樣前面也說過,groovy也不關(guān)心變量和方法參數(shù)的具體類型,。 (3) 不需要getter/setter方法 不要奇怪,,在很多ide(如eclipse)早就可以為序員自動產(chǎn)生getter/setter方法了。在groovy中,,則徹底不需要getter/setter方法——所有類成員(如果是默認(rèn)的public)根本不用通過getter/setter方法引用它們(當(dāng)然,,如果你一定要通過get/set方法訪問成員屬性,groovy也提供了它們),。 (4) 不需要構(gòu)造函數(shù) 不在需要程序員聲明任何構(gòu)造函數(shù),,因為groovy自動提供了足夠你使用的構(gòu)造函數(shù)。不用擔(dān)心構(gòu)造函數(shù)不夠多,,因為實際上只需要兩個構(gòu)造函數(shù)(1個不帶參數(shù)的默認(rèn)構(gòu)造函數(shù),,1個只帶一個map參數(shù)的構(gòu)造函數(shù)—由于是map類型,通過這個參數(shù)你可以在構(gòu)造對象時任意初始化它的成員變量),。 (5) 不需要return Groovy中,,方法不需要return來返回值嗎?這個似乎很難理解,??春竺娴拇a吧。 因此,,groovy風(fēng)格的類是這樣的: (6) 不需要()號 Groovy中方法調(diào)用可以省略()號(構(gòu)造函數(shù)除外),,也就是說下面兩句是等同的:
person1.setName 'kk'
person1.setName('kk')
下面看一個完整類定義的例子: class Person { def name def age String toString(){//注意方法的類型String,因為我們要覆蓋的方法為String類型 "$name,$age" } 如果你使用javabean風(fēng)格來做同樣的事,,起碼代碼量要增加1倍以上,。 我們可以使用默認(rèn)構(gòu)造方法實例化Person類: def person1=new Person() person1.name='kk' person1.age=20 println person1 也可以用groovy的風(fēng)格做同樣的事: def person2=new Person(['name':'gg','age':22]) //[]號可以省略 println person2
這樣需要注意我們覆蓋了Object的toString方法,因為我們想通過println person1這樣的方法簡單地打印對象的屬性值,。 然而toString 方法中并沒有return 一個String,,但不用擔(dān)心,Groovy 默認(rèn)返回方法的最后一行的值,。 13,、 ?運算符 在java中,,有時候為了避免出現(xiàn)空指針異常,,我們通常需要這樣的技巧: if(rs!=null){ rs.next() … … } 在groovy中,可以使用操作符達(dá)到同樣的目的: rs?.next() 在這里是一個條件運算符,,如果前面的對象非null,,執(zhí)行后面的方法,否則什么也不做,。 14,、 可變參數(shù) 等同于java 5中的變長參數(shù)。首先我們定義一個變長參數(shù)的方法sum: int sum(int... var) { def total = 0 for (i in var) total += i return total } 我們可以在調(diào)用sum時使用任意個數(shù)的參數(shù)(1個,,2個,,3個……): println sum(1) println sum(1,2) println sum(1,2,3) 15、 枚舉 定義一個enum: enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } 然后我們在switch語句中使用他: 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?" } 注意,,switch和case中可以使用任何對象,,尤其是可以在case中使用List和范圍,,從而使分支滿足多個條件(這點跟delphi有點象)。 同java5一樣,,groovy支持帶構(gòu)造器,、屬性和方法的enum: 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() 16、 Elvis操作符 這是三目運算符“:”的簡單形式,,三目運算符通常以這種形式出現(xiàn): String displayName = name != null ? name : "Unknown"; 在groovy中,,也可以簡化為(因為null在groovy中可以轉(zhuǎn)化為布爾值false): String displayName = name ? name : "Unknown"; 基于“不重復(fù)”的原則,可以使用elvis操作符再次簡化為: String displayName = name ?: "Unknown" 17,、 動態(tài)性 Groovy所有的對象都有一個元類metaClass,,我們可以通過metaClass屬性訪問該元類。通過元類,,可以為這個對象增加方法(在java中不可想象),!見下面的代碼,msg是一個String,通過元類,,我們?yōu)?/span>msg增加了一個String 類中所沒有的方法up: def msg = "Hello!" println msg.metaClass String.metaClass.up = { delegate.toUpperCase() } println msg.up() 通過元類,,我們還可以檢索對象所擁有的方法和屬性(就象反射): msg.metaClass.methods.each { println it.name } msg.metaClass.properties.each { println it.name } 甚至我們可以看到我們剛才添加的up方法。 我們可以通過元類判斷有沒有一個叫up的方法,,然后再調(diào)用它: if (msg.metaClass.respondsTo(msg, 'up')) { println msg.toUpperCase() } 當(dāng)然,,也可以推斷它有沒有一個叫bytes的屬性: if (msg.metaClass.hasProperty(msg, 'bytes')) { println msg.bytes.encodeBase64() } 18、 Groovy swing 到現(xiàn)在為止,,我們的groovy一直都在控制臺窗口下工作,。如果你還不滿足,當(dāng)然也可以使用swingbuilder來構(gòu)建程序: 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() 怎么樣,?是不是跟java 中寫swing程序很象,?
五、單元測試 1,、 添加junit 使用 Build PathàAdd Libraries... 把junit添加到項目中,。 2、 新建測試 使用 New à Junit Test Case 新建測試?yán)蹋?/span>PersonTest,,在Class under test右邊的Browser按鈕,,選擇要進行測試的groovy類Person。 Finish,,下面編寫測試用例代碼(我使用了Junit4): import org.junit.*; public class TestPerson { @Test public void testToString(){ Person p=new Person(); //注意因為groovy編譯Person時默認(rèn)所有屬性為private p.setName("ddd"); //所以用set方法設(shè)置name屬性而不用p.name=”ddd” p.setAge(18); Assert.assertEquals("ddd-18", p.toString()); } } 運行Run AsàJunit Test,,發(fā)現(xiàn)testToString通過測試。 3,、使用groovy書寫測試用例 除了使用Java來書寫測試用例以外,,我們也可以使用groovy書寫。 New à Other à Groovy à Groovy Class,寫一個類GroovyTestPerson: import org.junit.*;
class GroovyTestPerson { @Test void testToString(){ Person p=new Person("name":"ddd","age":18) Assert.assertEquals("ddd-18", p.toString()) } } 可以看到,,這里使用的完全是Groovy風(fēng)格的書寫方式:不需要public,,使用map參數(shù)構(gòu)造對象。然而當(dāng)你Run AsàJunit Test的時候,,結(jié)果跟用java編寫的測試用例沒有什么兩樣,。 這也充分說明了,groovy和java,,除了語法不一樣,,本質(zhì)上沒有什么區(qū)別(對比.net framework中的C#和VB.net,,它們除了語法不同外,,本質(zhì)上它們都使用CLR)。
|
|