1

I'm trying to add a new method to my Groovy class dynamically, following the documentation. So here is my class which implements the methodMissing method:

class AlexTest {

    def methodMissing(String name, args){
        println "Method missing is called"

        def cachedMethod = { Object[] varArgs ->
            println "Hi! ${varArgs}"
        }

        AlexTest.metaClass."${name}" = cachedMethod
        return cachedMethod(args)
    }
}

and here is another groovy script which uses my AlexTest class:

def alexTest = new AlexTest()
alexTest.hi("Alex")
alexTest.hi("John")

I expect the "Method missing is called" to be called only once - as the method "hi" would have been 'introduced' inside the methodMissing. But, that methodMissing is being called twice, as if the "hi" method never gets introduced to the AlexTest class.

I have also tried to do it differently:

class AlexTest {

    AlexTest() {
        def mc = new ExpandoMetaClass(AlexTest, false, true)
        mc.initialize()
        this.metaClass = mc
    }

    def methodMissing(String name, args){
        println "Method missing is called"

        def cachedMethod = { Object[] varArgs ->
            println "Hi! ${varArgs}"
        }

        // note that this is calling metaClass inside the instance
        this.metaClass."${name}" = cachedMethod
        return cachedMethod(args)
    }
}

which sort of works, but only for that single instance. So if I create two instances of AlexTest, and call the 'hi' method, I will get two "Method missing is called" message. Can anyone point me to the documentation which explains this behaviour?

Thanks in advance!

Regards,

Alex

4

3 回答 3

2

Add the following to the beginning of your first attempt:

ExpandoMetaClass.enableGlobally()

It works for me with V 2.0.1

If you prefer to enable on a class instance basis, this post illustrates one way to do it:

class AlexTest {
    AlexTest() {
        def mc = new ExpandoMetaClass(AlexTest, false, true)
        mc.initialize()
        this.metaClass = mc        
    }

    def methodMissing(String name, args){
        println "Method missing is called"

        def cachedMethod = { Object[] varArgs ->
            println "Hi! ${varArgs}"
        }

        this.metaClass."${name}" = cachedMethod
        return cachedMethod(args)
    }
}

def alexTest = new AlexTest()
alexTest.hi("Alex")
alexTest.hi("John")
于 2013-09-11T02:32:28.597 回答
1

How about this in the script? This makes sure the methodMissing is implemented for the Class reference and is applied to all of the instances referring to it.

class AlexTest {

}

mc = AlexTest.metaClass
mc.methodMissing = {String name, args=[:] ->
        println "Method missing is called"
        def cachedMethod = { Object[] varArgs ->
            println "Hi! ${varArgs}"
        }
        mc."${name}" = cachedMethod
        cachedMethod(args)
}

def alexTest = new AlexTest()
alexTest.hi("Alex")
alexTest.hi("John")

def myTest = new AlexTest()
myTest.hi("Walter")
myTest.hi("Hank")

//Prints
Method missing is called
Hi! [Alex]
Hi! [John]
Hi! [Walter]
Hi! [Hank]

Although enabling ExpandoMetaClass globally works as well with a cost of little extra memory. :)

于 2013-09-11T02:48:25.423 回答
0

Another solution is caching the method closures in a Map.

class AlexTest {
    static Map methods = [:]

    def methodMissing(String name, args){
        if (!methods[name]) {
            println "Method is not cached"
            methods[name] = { Object[] varArgs ->
                println "Hi! ${varArgs}"
            }
        }
        return methods[name](args)
    }
}

def alexTest = new AlexTest()
alexTest.hi("Alex")
alexTest.hi("John")

Output

Method is not cached
Hi! [Alex]
Hi! [John]
于 2021-08-12T01:55:36.457 回答