First they ignore you
then they ridicule you
then they fight you
then you win
    -- Mahatma Gandhi
Chinese => English     英文 => 中文             
随笔-221  评论-1047  文章-0  trackbacks-0
从Groovy1.1beta-2开始,实现动态改变对象的能力变的十分简单:

一开始,我们有这样一个类:
class Person {
    String name
}

该类的实例都是哑巴,不能说话,作为造物主的我们该完善它们,使它们能自我介绍(添加实例方法)
class Person {
    String name
}

// 添加自我介绍的行为
Person.metaClass.introduce << {println "I'm $name"}

现在让我们看看,它们到底是否真的能够开口自我介绍了呢:
class Person {
    String name
}

// 添加自我介绍的行为
Person.metaClass.introduce << {println "I'm $name"}
def person 
= new Person(name:"山风小子")
person.introduce()

运行结果:
I'm 山风小子

嗯~人类改造成功~

但人应该有性别吧,嗯~对的,加个性别属性sex(添加属性)
class Person {
    String name
}

// 添加自我介绍的行为
Person.metaClass.introduce << {println "I'm $name"}
// 添加性别属性,默认为男(Male)
Person.metaClass.sex = "Male"
def person 
= new Person(name:"山风小子")
person.introduce()
println person.sex

运行结果:
I'm 山风小子
Male


但做男人累啊~为了买房,娶妻拼命赚钱,做女人算了,做变性手术:
class Person {
    String name
}

// 添加自我介绍的行为
Person.metaClass.introduce << {println "I'm $name"}
// 添加性别属性,默认为男(Male)
Person.metaClass.sex = "Male"
def person 
= new Person(name:"山风小子")
person.introduce()
println person.sex
// 做变性手术,变为女的(Female)
person.sex = "Female"
println person.sex

运行结果:
I'm 山风小子
Male
Female

作为造物主的我们考虑到手术的风险性,为了让其他人知道自己现在是个女的,在介绍中添加性别说明:
class Person {
    String name
}

// 添加自我介绍的行为
Person.metaClass.introduce << {println "I'm $name"}
// 添加性别属性,默认为男(Male)
Person.metaClass.sex = "Male"
// 修改之前自我介绍行为,添加性别说明
Person.metaClass.introduce << {println "I'm $name, $sex"}
def person 
= new Person(name:"山风小子")
person.introduce()
// 做变性手术,变为女的(Female)
person.sex = "Female"
person.introduce()

运行结果:
I'm 山风小子, Male
I'm 山风小子, Female

为了造人方便点,搞个工厂方法(添加类方法,即静态方法)
class Person {
    String name
}
// 添加自我介绍的行为
Person.metaClass.introduce << {println "I'm $name"}
// 添加性别属性,默认为男(Male)
Person.metaClass.sex = "Male"
// 修改之前自我介绍行为,添加性别说明
Person.metaClass.introduce << {println "I'm $name, $sex"}
def person 
= new Person(name:"山风小子")
person.introduce()
// 做变性手术,变为女的(Female)
person.sex = "Female"
person.introduce()
// 工厂方法,造人方便点
Person.metaClass.'static'.createPerson = { name, sex ->
    Person p 
= new Person()
    p.name 
= name
    p.sex 
= sex
    
return p
}
def bluesun 
= Person.createPerson("山风小子""Male")
bluesun.introduce()

运行结果:
I'm 山风小子, Male
I'm 山风小子, Female
I'm 山风小子, Male

为了方便实例化Person,添加一个构造方法(添加构造方法)
class Person {
    String name
}
// 添加自我介绍的行为
Person.metaClass.introduce << {println "I'm $name"}
// 添加性别属性,默认为男(Male)
Person.metaClass.sex = "Male"
// 修改之前自我介绍行为,添加性别说明
Person.metaClass.introduce << {println "I'm $name, $sex"}
def person 
= new Person(name:"山风小子")
person.introduce()
// 做变性手术,变为女的(Female)
person.sex = "Female"
person.introduce()
// 工厂方法,造人方便点
Person.metaClass.'static'.createPerson = { name, sex ->
    Person p 
= new Person()
    p.name 
= name
    p.sex 
= sex
    
return p
}
def bluesun 
= Person.createPerson("山风小子""Male")
bluesun.introduce()
// 方便实例化Person,添加一个构造方法
Person.metaClass.constructor << { name, sex ->
    
new Person(name:name, sex:sex)
}
def daniel 
= new Person("Daniel""Male")
daniel.introduce()

运行结果:
I'm 山风小子, Male
I'm 山风小子, Female
I'm 山风小子, Male
I'm Daniel, Male

最后,引用一个官方例子swapCase来展示一下Groovy是如何增强既有类的能力的(演示如何使用delegate,注意演示的是final类:String)
String.metaClass.swapCase = {->
      def sb 
= new StringBuffer()
      
// delegate与this类似,引用当前正被‘改造’的对象
      delegate.each {
           sb 
<< (Character.isUpperCase(it as char? Character.toLowerCase(it as char) : 
                   Character.toUpperCase(it as 
char))
      }
      sb.toString()
}

String s 
= "Hello, world!"
println s.swapCase()

运行结果:
hELLO, WORLD!

<<用于添加方法(如果方法已经存在,会发生groovy.lang.GroovyRuntimeException异常),=用于添加方法或覆盖既有方法



而从Groovy1.1beta-3开始,Groovy的动态性有了进一步的增强:

我们可以通过respondsTo和hasProperty方法来判断是否存在某个方法和某个属性:
class Person {
    String name

    
public Person(name) {
        
this.name = name
    }

    def introduce() {
        println 
"I'm $name"
    }

    def introduce(String name) {
        println 
"She is $name"
    }
}

def daniel 
= new Person('Daniel')

// 判断实例daniel是否有方法introduce()
if (daniel.metaClass.respondsTo(daniel, 'introduce')) {
    daniel.introduce()
}

// 判断实例daniel是否有方法introduce(String)
if (daniel.metaClass.respondsTo(daniel, 'introduce', String)) {
    daniel.introduce(
'Annie')
}

// 判断实例daniel是否有属性name
if (daniel.metaClass.hasProperty(daniel, 'name')) {
    println daniel.name
}

运行结果:
I'm Daniel
She is Annie
Daniel


使用methodMissing方法来处理那些不存在的方法的调用
class Person {
    String name

    
public Person(name) {
        
this.name = name
    }

    def introduce() {
        println 
"I'm $name"
    }

    def introduce(String name) {
        println 
"She is $name"
    }
}

Person.metaClass.methodMissing 
= { name, args ->
    
// 动态添加方法
    Person.metaClass."$name" = { methodArgs ->
        
if ("hello".equals(name))
            println 
"$methodArgs"
        
else {
            def argList 
= Arrays.asList(methodArgs)
            println 
"No method $name with ${argList*.class}"
        }
    }

   delegate.
"$name"(args)
}

def daniel 
= new Person('Daniel')

daniel.hello(
"Leona")
daniel.hi(
"Annie""Leona")

运行结果:
{"Leona"}
No method hi with [class java.lang.String, class java.lang.String]


类似地,使用propertyMissing方法来处理那些不存在的属性的引用
class Person {
    String name

    
public Person(name) {
        
this.name = name
    }

    def introduce() {
        println 
"I'm $name"
    }

    def introduce(String name) {
        println 
"She is $name"
    }
}

Person.metaClass.propertyMissing 
= { String name, value ->
    
// 动态添加属性
    Person.metaClass."$name" = value
}

def daniel 
= new Person('Daniel')
daniel.sex 
= 'Male'
println daniel.sex

运行结果:
Male


而从Groovy1.6beta-2-snapshot开始,Groovy在这方面的语法更加简洁了(当然原来prototype式的语法也同样支持):

String.metaClass {
    
'static' {
        value 
= {
            it.toString()
        }
    }
    
    swapCase 
= { ->
      def sb 
= new StringBuffer()
      
// delegate与this类似,引用当前正被‘改造’的对象
      delegate.each {
           sb 
<< (Character.isUpperCase(it as char? Character.toLowerCase(it as char) : 
                   Character.toUpperCase(it as 
char))
      }
      sb.toString()
    }
}

String s 
= 'Hello, world!'
println s.swapCase()
println String.value(
new Date())

运行结果:
hELLO, WORLD!
Sun Jun 08 14:53:28 CST 2008



附:朝花夕拾——Groovy & Grails
posted on 2007-07-15 00:50 山风小子 阅读(4381) 评论(12)  编辑  收藏 所属分类: Groovy & Grails