【1】Groovy語言學習:groovy語言簡介及基本語法
Groovy是一種基於JVM的敏捷開發語言,它結合了Python、Ruby和Smalltalk的許多強大的特性。
一、groovy是什麼
簡單地說,Groovy 是下一代的java語言,跟java一樣,它也執行在 JVM 中。作為跑在JVM中的另一種語言,groovy語法與 Java 語言的語法很相似。同時,Groovy 拋棄了java煩瑣的文法。同樣的語句,使用groovy能在最大限度上減少你的擊鍵次數。
二、Groovy語法簡介
1、 沒有型別的java
作為動態語言,groovy中所有的變數都是物件(類似於.net framework,所有物件繼承自java.lang.Object),在宣告一個變數時,groovy不要求強制型別宣告,僅僅要求變數名前使用關鍵字def(從groovy jsr 1開始,在以前的版本中,甚至連def都不需要)。
def var = 'hello'
println(var)
println var
println(var.class)
def var2 = "hello2"
println(var2)
println var2
println(var2.class)
列印結果如下:
hello
hello
class java.lang.String
hello2
hello2
class java.lang.String
Process finished with exit code 0
你可以看到程式最後輸出了var的實際型別為:java.lang.String
作為例外,方法引數和迴圈變數的宣告不需要def。
2、 不需要public
實際上,groovy中預設的修飾符就是public,所以public修飾符你根本就不需要寫,這點跟java不一樣。
3、 不需要語句結束符
Groovy中沒有語句結束符,當然為了與java保持一致性,你也可以使用;號作為語句結束符。在前面的每一句程式碼後面加上;號結束,程式同樣正常執行(為了接受java程式設計師的頑固習慣)。
4、 字串連線符
跟java一樣,如果你需要把一個字串寫在多行裡,可以使用+號連線字串。程式碼可以這樣寫:
def var = "hello"+
"world"+
",beijing"
println(var)
def var2 = """hello
world
,shanghai"""
println(var2)
說明:三個”號之間不在需要+號進行連線(不過字串中的格式符都會被保留,包括回車和tab)。
列印結果如下:
helloworld,beijing
hello
world
,shanghai
5、 一切皆物件
聽起來象是“眾生平等”的味道,事實上groovy對於物件是什麼型別並不關心,一個變數的型別在執行中隨時可以改變,一切根據需要而定。如果你賦給它boolean ,那麼不管它原來是什麼型別,它接受boolean值之後就會自動把型別轉變為boolean值。看下面的程式碼:
def var = "hello "+
"world"+
",groovy!"
println var;
println var.class;
var = 1001
println var.class
列印結果:
hello world,groovy!
class java.lang.String
class java.lang.Integer
var這個變數在程式執行中,型別在改變。一開始給它賦值String,它的型別就是String,後面給它賦值Integer,它又轉變為Integer。
6、 迴圈
刪除整個原始檔內容,用以下程式碼替代:
def var = "hello "+
"world"+
",groovy!"
def repeat(var){
for(i = 0; i < 5; i++){
println(var)
}
}
repeat(var)
println("-------------")
def repeat2(var){
for(i in 0..5){
println(var)
}
}
repeat2(var)
列印結果:
hello world,groovy!
hello world,groovy!
hello world,groovy!
hello world,groovy!
hello world,groovy!
-------------
hello world,groovy!
hello world,groovy!
hello world,groovy!
hello world,groovy!
hello world,groovy!
注意迴圈變數i前面沒有def。當然也沒有java中常見的int,但如果你非要加上int也不會有錯,因為從Groovy1.1beta2之後開始(不包括1.1beta2),groovy開始支援java經典的for迴圈寫法。
此外,上面的for語句還可以寫成:for(i in 0..5)
這樣的結果是一樣的。
7、 String 和 Gstring
除了標準的java.lang.String以外(用’號括住),groovy還支援Gstring字串型別(用“號括住)。把上面的for迴圈中的語句改成:
def repeat(var){
for(i in 0..5){
// println(var)
println("$var:$i")
}
}
repeat(var2)
執行一下,你就會明白什麼是Gstring。
再舉一個例子:
def str1 = "程式語言"
def str2 = "Groovy"
println "$str1:$str2"
println '$str1:$str2'
展示結果為:
程式語言:Groovy
$str1:$str2
8、 範圍
這個跟pascal中的“子界”是一樣的。在前面的for迴圈介紹中我們已經使用過的for(i in 0..5)這樣的用法,其中的0..5就是一個範圍。
範圍 是一系列的值。例如 “0..4” 表明包含 整數 0、1、2、3、4。Groovy 還支援排除範圍,“0..<4” 表示 0、1、2、3。還可以建立字元範圍:“a..e” 相當於 a、b、c、d、e。“a..
9、預設引數值
可以為方法指定預設引數值。我們修改repeat方法的定義:
def repeat(var, time = 3){
for(i = 0;i < time;i++){
// println(var)
println("$var:$i")
}
}
repeat(var2)
可以看到,repeat方法增加了一個引數repeat(並且給了一個預設值3),用於指定迴圈次數。當我們不指定第2個引數呼叫repeat方法時,repeat引數取預設值3。
10、Collection集合
Groovy支援最常見的兩個java集合:java.util.Collection和java.util.Map。前面所說的範圍實際也是集合的一種(java.util.List)。
Collection集合:
//1、定義一個集合
def collect = ["a","b","c"]
//2、給集合增加元素
collect.add(1);
collect << "come on";
collect[collect.size()] = 100.0
//3、集合索引
println collect[collect.size()-1]
println collect
println collect.size()
//4、負索引
println collect[-1] //索引其倒數第1個元素
println collect[-2] //索引其倒數第2個元素
//5、集合運算:
collect=collect+5 //在集合中新增元素5
println collect[collect.size()-1]
collect=collect-'a' //在集合中減去元素a(第1個)
println collect[0] //現在第1個元素變成b了
//6、往集合中新增另一個集合或刪除一個集合:
collect=collect-collect[0..4] //把集合中的前5個元素去掉
println collect[0] //現在集合中僅有一個元素,即原來的最後一個元素
println collect[-1] //也可以用負索引,證明最後一個元素就是第一個元素
列印結果就不展示了。
11、Map集合
Map是“鍵-值”對的集合,在groovy中,鍵不一定是String,可以是任何物件(實際上Groovy中的Map就是java.util.LinkedHashMap)。
//1、定義一個Map:
def map = ['name':'john','age':14,'sex':'boy']
println map
//2、新增項:
map = map+['weight':25] //新增john的體重
map.put('length',1.27) //新增john的身高
map.father='Keller' //新增john的父親
println map
//3、兩種方式檢索值:
println map['father'] //通過key作為下標索引
println map.length //通過key作為成員名索引
列印如下:
[name:john, age:14, sex:boy]
[name:john, age:14, sex:boy, weight:25, length:1.27, father:Keller]
Keller
1.27
12、 閉包(Closure)
閉包是用{符號括起來的程式碼塊,它可以被單獨執行或呼叫,也可以被命名。類似‘匿名類’或行內函數的概念。
閉包中最常見的應用是對集合進行迭代,下面定義了3個閉包對map進行了迭代:
def map = ['name':'john','age':14,'sex':'boy']
map.each(
{key,value-> // key,value兩個引數用於接受每個元素的鍵/值
println "$key:$value"})
map.each{println it} //it是一個關鍵字,代表map集合的每個元素
map.each({ println it.getKey()+"-->"+it.getValue()})
列印如下:
name:john
age:14
sex:boy
name=john
age=14
sex=boy
name-->john
age-->14
sex-->boy
另外:
//除了用於迭代之外,閉包也可以單獨定義,例如定義一個必包:
def say={word->
println "Hi,$word!"
}
//呼叫:
say('groovy')
say.call('groovy&grails') //call是必包的方法
列印如下:
Hi,groovy!
Hi,groovy&grails!
看起來,閉包類似於方法,需要定義引數和要執行的語句,它也可以通過名稱被呼叫。然而閉包物件(不要奇怪,閉包也是物件)可以作為引數傳遞(比如前面的閉包作為引數傳遞給了map的each方法)。而在java中,要做到這一點並不容易(也許C++中的函式指標可以,但不要忘記java中沒有指標)。其次,閉包也可以不命名(當然作為代價,只能在定義閉包時執行一次),而方法不可以。
13、 類
Groovy類和java類一樣,你完全可以用標準java bean的語法定義一個groovy 類。但作為另一種語言,我們可以使用更groovy的方式定義和使用類,這樣的好處是,你可以少寫一半以上的javabean程式碼:
(1) 不需要public修飾符
如前面所言,groovy的預設訪問修飾符就是public,如果你的groovy類成員需要public修飾,則你根本不用寫它。
(2) 不需要型別說明
同樣前面也說過,groovy也不關心變數和方法引數的具體型別。
(3) 不需要getter/setter方法
不要奇怪,在很多ide(如eclipse)早就可以為序員自動產生getter/setter方法了。在groovy中,則徹底不需要getter/setter方法——所有類成員(如果是預設的public)根本不用通過getter/setter方法引用它們(當然,如果你一定要通過get/set方法訪問成員屬性,groovy也提供了它們)。
(4) 不需要建構函式
不在需要程式設計師宣告任何建構函式,因為groovy自動提供了足夠你使用的建構函式。不用擔心建構函式不夠多,因為實際上只需要兩個建構函式(1個不帶引數的預設建構函式,1個只帶一個map引數的建構函式—由於是map型別,通過這個引數你可以在構造物件時任意初始化它的成員變數)。
(5) 不需要return
Groovy中,方法不需要return來返回值嗎?這個似乎很難理解。看後面的程式碼吧。
因此,groovy風格的類是這樣的:
(6) 不需要()號
Groovy中方法呼叫可以省略()號(建構函式除外),也就是說下面兩句是等同的:
person1.setName 'kk'
person1.setName('kk')
下面看一個完整類定義的例子:
class Person {
def name
def age
String toString(){ //注意方法的型別String,因為我們要覆蓋的方法為String型別
"$name,$age"
}
}
// 我們可以使用預設構造方法例項化Person類:
def person1 = new Person()
person1.name='kk'
person1.age=20
println person1
// 也可以用groovy的風格做同樣的事:
def person2 = new Person(['name':'gg','age':22]) //[]號可以省略
println person2
列印如下:
kk,20
gg,22
這樣需要注意我們覆蓋了Object的toString方法,因為我們想通過println person1這樣的方法簡單地列印物件的屬性值。
然而toString 方法中並沒有return 一個String,但不用擔心,Groovy 預設返回方法的最後一行的值。
14、?運算子
在java中,有時候為了避免出現空指標異常,我們通常需要這樣的技巧:
if(rs!=null){
rs.next()
… …
}
在groovy中,可以使用?操作符達到同樣的目的:
rs?.next()
?在這裡是一個條件運算子,如果?前面的物件非null,執行後面的方法,否則什麼也不做。
15、可變長引數
等同於java 5中的變長引數。首先我們定義一個變長引數的方法sum:
int sum(int... var) {
def total = 0
for (i in var)
total += i
return total
}
//我們可以在呼叫sum時使用任意個數的引數(1個,2個,3個……):
println sum(1)
println sum(1,2)
println sum(1,2,3)
println sum(1,2,9,10,55)
16、列舉
//1、定義一個enum:
enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
//2、然後我們在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?"
}
//3、注意,switch和case中可以使用任何物件,尤其是可以在case中使用List和範圍,從而使分支滿足多個條件(這點跟delphi有點象)。
//4、同java5一樣,groovy支援帶構造器、屬性和方法的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()
Planet.JUPITER.printMe()
列印如下:
Weekends are cool
EARTH has a mass of 5.976E24 and a radius of 6378140.0
JUPITER has a mass of 1.9E27 and a radius of 7.1492E7
17、Elvis操作符
這是三目運算子“?:”的簡單形式,三目運算子通常以這種形式出現:
String displayName = name != null ? name : "Unknown";
在groovy中,也可以簡化為(因為null在groovy中可以轉化為布林值false):
String displayName = name ? name : "Unknown";
基於“不重複”的原則,可以使用elvis操作符再次簡化為:
String displayName = name ?: "Unknown"
18、動態性
Groovy所有的物件都有一個元類metaClass,我們可以通過metaClass屬性訪問該元類。通過元類,可以為這個物件增加方法(在java中不可想象)!見下面的程式碼,msg是一個String,通過元類,我們為msg增加了一個String 類中所沒有的方法up:
def msg = "Hello!"
println msg.metaClass
String.metaClass.up = { delegate.toUpperCase() }
println msg.up()
//2、通過元類,我們還可以檢索物件所擁有的方法和屬性(就象反射):
msg.metaClass.methods.each { println it.name }
msg.metaClass.properties.each { println it.name }
//3、甚至我們可以看到我們剛才新增的up方法。
//4、我們可以通過元類判斷有沒有一個叫up的方法,然後再呼叫它:
if (msg.metaClass.respondsTo(msg, 'up')) {
println msg.toUpperCase()
}
//5、當然,也可以推斷它有沒有一個叫bytes的屬性:
if (msg.metaClass.hasProperty(msg, 'bytes')) {
println msg.bytes.encodeBase64()
}