Groovy語法(四):面向對象

一、groovy中類,接口等的定義和使用

類的定義與使用

//在idea中直接新建一個Groovy Class

/**
 * groovy中默認都是public,groovy類最終都會繼承GroovyObject
 */
class Person {
    //默認生成get/set方法
    String name
    Integer age

    //方法也默認是public的,def表明方法的返回類型是Object的
    def increaseAge(Integer years) {
        this.age += years
    }
}


//在腳本文件中使用定義好的類

//沒有定義構造方法,也可以在創建對象是傳入初始值,可傳可不傳
def person = new Person(name: 'groovy', age: 6)
println "the name is $person.name,the age is $person.age"  //the name is groovy,the age is 6

person.increaseAge(5)

//無論使用.還是get/set最終都會調用get/set方法
println person.age //11

接口的定義與使用

/**
 * groovy中接口不允許定義非public類型的方法
 */
interface Action {
    void eat()

    void work()

    void sleep()
}


//與java中一致
class Person implements Action{
    //默認生成get/set方法
    String name
    Integer age

    //方法也默認是public的,def表明方法的返回類型是Object的
    def increaseAge(Integer years) {
        this.age += years
    }

    @Override
    void eat() {}

    @Override
    void work() {}

    @Override
    void sleep() {}
}

Trait的定義與使用

//Trait 和接口幾乎一模一樣,只不過trait可以給接口提供默認的實現,
//相當於java中接口的適配器模式,直接繼承接口就需要實現該接口的所有方法,但是很多時候我們裏面有很多方法使我們不需要的,這個時候就可以定義一個適配器實現這個接口,給我們不需要的方法提供一個默認實現,這樣我們就可以只關注我們需要的方法了

trait DefaultAction {
    abstract void eat()

    void work() {
        println 'i can work'
    }

    abstract void sleep()
}

class Person2 implements DefaultAction {

    @Override
    void eat() {}

    @Override
    void sleep() {}
}

def person2 = new Person2()
person2.work() //i can work

二、 groovy中的元編程

什麼是元編程?

簡單的理解就是編寫代碼執行的時期,比如由編譯執行的java,運行時執行的代碼(java反射)
groovy中運行時執行時序圖

在這裏插入圖片描述

class Person1 {
    def invokeMethod(String name, Object args) {
        return "the method is $name,the params is $args"
    }
}

class Person2 {
    def methodMissing(String name, Object args) {
        return "the method $name is missing"
    }
}


class Person3 {

    def invokeMethod(String name, Object args) {
        return "the method is ${name},the params is ${args}"
    }

    def methodMissing(String name, Object args) {
        return "the method ${name} is missing"
    }
}

Person1 person1 = new Person1()
Person2 person2 = new Person2()
Person3 person3 = new Person3()

println person1.cry() //the method is cry,the params is []
println person2.cry() //the method cry is missing
println person3.cry() //the method cry is missing

//可以看到符合運行時時序圖,當調用一個類中不存在的方法時,若重寫了invokeMethod方法或者methodMissing方法時,會調用該方法(methodMissing的優先級更高)

MetaClass
前面的示例說明了groovy的運行時執行時序,那麼當中的MetaClass又是什麼?
我們都知道在groovy中,所有的對象都實現了GroovyObject接口

public interface GroovyObject {
public Object invokeMethod(String name, Object args);
public Object getProperty(String property);
public void setProperty(String property, Object newValue);
public MetaClass getMetaClass();
public void setMetaClass(MetaClass metaClass);
}

每個對象都有一個MetaClass,MetaClass是Groovy元概念的核心,它提供了一個Groovy類的所有的元數據,如可用的方法、屬性列表等.

MetaClass的作用

爲類在運行時動態的添加屬性或者方法
1.添加屬性

class Person1 {}

Person1 person1 = new Person1()
person1.metaClass.sex = 'male'
println person1.sex //輸出結果:male
person1.sex = "female"
println "the new sex is $person1.sex"  //輸出結果:the new sex is female

//可以看到 Person1中並沒有sex這個屬性,而我們通過metaClass動態的添加了一個sex屬性,並且能夠正確的輸出和修改

2.添加方法

class Person1 {}

Person1 person1 = new Person1()
person1.metaClass.sex = 'male'
person1.metaClass.sexUpperCase = { -> sex.toUpperCase() }
println person1.sexUpperCase()  //輸出結果:MALE

3.添加靜態方法

class Person {
    def name
    def age
}

//添加靜態方法
Person.metaClass.static.createPerson = { String name, int age -> new Person(name: name, age: age) }

def person = Person.createPerson("groovy",6)
println person.name //輸出結果:groovy

使用場景
使用每個類的MetaClass可以不修改任何類本身的代碼,不通過繼承類來擴展方法來動態的添加屬性和方法。比如使用第三方庫的時候,想要修改某些類,而類又是final的,即可通過MetaClass動態的注入屬性和方法變相的達到修改的目的

ExpandoMetaClass.enableGlobally()
在一個腳本中動態注入的屬性和方法,在其它腳本是不能直接使用的,想要使用需要在該腳本中重新注入。這是比較繁瑣的,比如我們想要對String類型進行某一些操作,能不能爲String注入方法,全局都能使用呢?答案是能,通過ExpandoMetaClass.enableGlobally()

//定義一個Person class
class Person {
    String name
    Integer age
}

//定義一個程序管理類 Application class
class Application {
    static void init() {
        //讓注入的方法全局生效
        ExpandoMetaClass.enableGlobally()
        //注入一個靜態方法(假設這裏是爲第三方類庫注入了我們需要的方法)
        Person.metaClass.static.createPerson = { String name, Integer age -> new Person(name: name, age: age) }
    }
}

//定義另外一個類PersonManager class,在該類中使用Application class中爲Person類注入的方法
class PersonManager {
    static Person createPerson(String name, Integer age) {
        //在另外一個腳本中使用注入的方法
        return Person.createPerson(name, age)
    }
}

//新的入口類 Entry class驗證ExpandoMetaClass.enableGlobally()之後注入的方法可以全局調用
class Entry {
    static void main(String[] args) {
        println '程序開始...'
        Application.init() //注入
        Person p = PersonManager.createPerson('groovy', 6)
        println "the name is $p.name,the age is $p.age"

        //輸出結果:
        /**
         * 程序開始...
         * the name is groovy,the age is 6
         */
    }
}
發佈了40 篇原創文章 · 獲贊 6 · 訪問量 4792
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章