前面說到groovy支持腳本和類,前面一節已將簡單說了腳本和類之間的關係,這一節主要介紹一下groovy中類的相關知識,即面向對象相關知識。 1.類型 1.1 原始類型 groovy中支持的原始數據類型與java相同,分別是boolean,char,short,int,long,float,doub ...
前面說到groovy支持腳本和類,前面一節已將簡單說了腳本和類之間的關係,這一節主要介紹一下groovy中類的相關知識,即面向對象相關知識。
1.類型
1.1 原始類型
groovy中支持的原始數據類型與java相同,分別是boolean,char,short,int,long,float,double。
1.2 類
groovy中的類與java中很相似,但有以下幾點是groovy特有的:
public
修飾的欄位會被自動轉換成屬性變數,這樣可以避免很多冗餘的get和set方法。- 如果屬性或方法沒有訪問許可權修飾符,那麼預設是public,而java中是proteced。
- 類名不需要和文件名相同。
- 一個文件中可以定義多個一級類。如沒有定義類,則這個groovy文件被認為是腳本文件。
1.2.1 普通類
groovy的普通類和java類似,使用new關鍵字獲得實例。
1.2.2 內部類
內部類也基本類似,下麵給一個例子:
class Outer2 {
private String privateStr = 'some string'
def startThread() {
new Thread(new Inner2()).start()
}
class Inner2 implements Runnable {
void run() {
println "${privateStr}."
}
}
}
1.2.3 抽象類
抽象類也與java基本類似:
abstract class Abstract {
String name
abstract def abstractMethod()
def concreteMethod() {
println 'concrete'
}
}
1.3 介面
groovy的介面和java也基本類似,支持介面繼承介面。
1.4 構造方法
groovy的構造方法和java就有略微不同了,groovy的構造方法支持位置參數
和命名參數
,下麵具體看。
1.4.1 位置參數構造方法
位置構造參數跟java中的通常構造方法類似,不同位置的參數具有不同的含義。如下:
class PersonConstructor {
String name
Integer age
PersonConstructor(name, age) {
this.name = name
this.age = age
}
}
def person1 = new PersonConstructor('Marie', 1)
def person2 = ['Marie', 2] as PersonConstructor
PersonConstructor person3 = ['Marie', 3]
具體調用構造方法的時候groovy多了兩種寫法。因為位置已經固定,所以即使PersonConstructor person3 = ['Marie', 3]
這樣的寫法groovy也能從內部給你做初始化。
1.4.2 命名參數構造方法
命名參數構造方法不需要用戶定義,當一個類沒有構造方法的時候,其預設有一個命名參數構造方法。
class PersonWOConstructor {
String name
Integer age
}
def person4 = new PersonWOConstructor()
def person5 = new PersonWOConstructor(name: 'Marie')
def person6 = new PersonWOConstructor(age: 1)
def person7 = new PersonWOConstructor(name: 'Marie', age: 2)
1.5 方法
定義groovy的方法也很簡單,可使用關鍵字def
或者返回值就行。groovy中的方法都有返回值,如果沒有寫return
語句,groovy會計算方法中的最後一行語句並將其結果返回。
下麵是四種不同的方法定義:
def someMethod() { 'method called' }
String anotherMethod() { 'another method called' }
def thirdMethod(param1) { "$param1 passed" }
static String fourthMethod(String param1) { "$param1 passed" }
1.5.1 方法的命名參數
在自定義的方法中要使用命名參數的話,就要使用Map作為唯一參數,如下:
def foo(Map args) { "${args.name}: ${args.age}" }
foo(name: 'Marie', age: 1)
1.5.2 方法的預設參數
groovy方法支持預設參數,這樣就是的其參數變得可選,當參數沒有被填入,則會使用預設參數:
def foo(Map args) { "${args.name}: ${args.age}" }
foo(name: 'Marie', age: 1)
1.5.3 方法的可變長參數
這個在java中也是存在的,舉個簡單的例子:
def foo(Map args) { "${args.name}: ${args.age}" }
foo(name: 'Marie', age: 1)
1.6 註解
groovy中的註解跟java中的類似,但又比java中多了一些特性,下麵簡單介紹一下。
1.6.1 註解的閉包參數
在groovy中,有一個有趣的語言特性就是可以使用閉包
作為註解的參數值。這樣的註解一般在什麼情況下使用呢?舉個簡單的例子,有些時候軟體的運行時依賴其運行的環境和操作系統的,針對不同的環境或系統,表現也不一樣。看一下這個例子:
class Tasks {
Set result = []
void alwaysExecuted() {
result << 1
}
@OnlyIf({ jdk>=6 })
void supportedOnlyInJDK6() {
result << 'JDK 6'
}
@OnlyIf({ jdk>=7 && windows })
void requiresJDK7AndWindows() {
result << 'JDK 7 Windows'
}
}
Tasks類用於完成alwaysExecuted
,supportedOnlyInJDK6
,requiresJDK7AndWindows
這三個任務,但不同的任務對環境和系統的要求都不一樣,這裡使用@OnlyIf
來表明對環境和系統的需求。
@Retention(RetentionPolicy.RUNTIME)
@interface OnlyIf {
Class value()
}
在groovy中如果需要讓註解接受閉包的話,只需要像上面這樣定義一個Class類型的value值。這樣OnlyIf就可以接受閉包作為其值了。
接著寫處理類:
class Runner {
static <T> T run(Class<T> taskClass) {
def tasks = taskClass.newInstance()
def params = [jdk:6, windows: false]
tasks.class.declaredMethods.each { m ->
if (Modifier.isPublic(m.modifiers) && m.parameterTypes.length == 0) {
def onlyIf = m.getAnnotation(OnlyIf)
if (onlyIf) {
Closure cl = onlyIf.value().newInstance(tasks,tasks)
cl.delegate = params
if (cl()) {
m.invoke(tasks)
}
} else {
m.invoke(tasks)
}
}
}
tasks
}
}
和java類似,通過反射拿到Task對象的方法,接著獲取其OnlyIf註解,如果獲取成功,則提取OnlyIf的閉包進行調用。
2 Traits(特征)
trait是groovy中獨有的面向對象的語法特性,他具備如下功能:
- 行為構成
- 運行時的介面實現
- 行為重載
- 相容靜態類型的檢查和編譯
Trait可以被看作是具有方法實現和狀態的介面,使用trait
關鍵字定義:
trait FlyingAbility {
String fly() { "I'm flying!" }
}
上面就定義了一個飛行能力的特證,它的使用方法和介面一樣,都是使用implements
關鍵字:
class Bird implements FlyingAbility {}
def b = new Bird()
assert b.fly() == "I'm flying!"
這個看上去感覺跟繼承有點類似,但又不一樣,trait僅僅是將其方法和狀態嵌入到實現類中,而沒有繼承中的那種上下級的父子關係。
trait中的一些語法特性:
- trait中支持定義抽象方法,其實現類必須實現此抽象方法。
- trait中可以定義私有方法,其實現類無法訪問。
- trait中的this關鍵字指其實現類。
- trait可以實現介面。
- trait中可定義屬性,此屬性會自動被附加到實現此trait的類中。
- trait可定義私有欄位由於存儲相關狀態。
- trait可定義公共欄位,但為了避免
鑽石問題
,其獲取方式有所不同,如下:
trait Named {
public String name
}
class Person implements Named {}
def p = new Person()
p.Named__name = 'Bob'
- 第一個類可以實現多個trait。
- 實現類可重寫trait中的預設方法。
- trait可以繼承另一個trait使用關鍵字extends,若要繼承多個則使用implements關鍵字。
- 可以在運行時動態實現trais,使用關鍵字as。
以上簡單介紹了groovy中面向對象的相關知識,更詳細的資料請參考官方文檔