Groovy中的面向對象

Groovy中的面向對象

前面說到groovy支持腳本和類,前面一節已將簡單說了腳本和類之間的關係,這一節主要介紹一下groovy中類的相關知識,即面向對象相關知識。html

1.類型

1.1 原始類型

groovy中支持的原始數據類型與java相同,分別是boolean,char,short,int,long,float,double。java

1.2 類

groovy中的類與java中很類似,但有如下幾點是groovy特有的:windows

  • public修飾的字段會被自動轉換成屬性變量,這樣能夠避免不少冗餘的get和set方法。
  • 若是屬性或方法沒有訪問權限修飾符,那麼默認是public,而java中是proteced。
  • 類名不須要和文件名相同。
  • 一個文件中能夠定義多個一級類。如沒有定義類,則這個groovy文件被認爲是腳本文件。

1.2.1 普通類

groovy的普通類和java相似,使用new關鍵字得到實例。閉包

1.2.2 內部類

內部類也基本相似,下面給一個例子:ui

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基本相似:this

abstract class Abstract {         
    String name

    abstract def abstractMethod() 

    def concreteMethod() {
        println 'concrete'
    }
}

1.3 接口

groovy的接口和java也基本相似,支持接口繼承接口。操作系統

1.4 構造方法

groovy的構造方法和java就有略微不一樣了,groovy的構造方法支持位置參數命名參數,下面具體看。code

1.4.1 位置參數構造方法

位置構造參數跟java中的一般構造方法相似,不一樣位置的參數具備不一樣的含義。以下:htm

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中面向對象的相關知識,更詳細的資料請參考官方文檔

相關文章
相關標籤/搜索