如何更改Groovy中方法委托的顺序?

如何更改Groovy中方法委托的顺序?,groovy,metaprogramming,Groovy,Metaprogramming,我的目标是更改Groovy中方法委托的顺序,以便可以动态重写Groovy对象中的方法 我有一个叫做武器的类,定义如下: class Weapon { Prefix prefix String method() { 'Called from Weapon' } } 其中,前缀是此类的一个实例: class Prefix { final Map<String, Closure> methods = [:] void prope

我的目标是更改Groovy中方法委托的顺序,以便可以动态重写Groovy对象中的方法

我有一个叫做
武器
的类,定义如下:

class Weapon {
    Prefix prefix

    String method() {
        'Called from Weapon'
    }
}
其中,
前缀
是此类的一个实例:

class Prefix {
    final Map<String, Closure> methods = [:]

    void propertyMissing(String name, Closure value) {
        if (value != null) {
            methods[name] = value
        }
        else {
            methods.remove(name)
        }
    }

    def methodMissing(String name, args) {
        if (!methods.containsKey(name)) {
            throw new MissingMethodException(name, Prefix, args)
        }
        methods.(name)(args)
    }
}
我对Groovy的元编程相当陌生,所以我并不完全了解它的所有功能。起初,我以为可以通过简单地重写武器中的
invokeMethod
方法来实现这一点,但Groovy最终在调用
invokeMethod
之前找到了该方法(我假设它是在元类或类中找到的)

我能想到的唯一解决方案是为
武器
创建一个自定义元类,它将首先检查
武器
的前缀。我的实现如下:

class WeaponMetaClass {
    WeaponMetaClass(MetaClass metaClass) {
        super(metaClass)
    }
    Object invokeMethod(Object object, String methodName, Object[] args) {
        if (object instanceof Weapon && object.prefix != null) {
            try {
                return object.prefix.(methodName)(args)
            }
            catch (MissingMethodException ignored) {
            }
        }
        return super.invokeMethod(object, methodName, args)
    }
}

但是,这不起作用,来自
武器
(带或不带
前缀
)的每个方法调用都返回null。我是否在实现中犯了错误,是否有更好的方法来实现这一点,或者这根本不可能?

您可以使用默认元类来实现所需的行为

class Weapon {
    void setPrefix(Map m){
        this.metaClass=null                     //reset to default class behavior
        if(m)m.each{k,v-> this.metaClass[k]=v}  //redefine methods/properties from map
    }

    String method() {
        'Called from Weapon'
    }
}

def w = new Weapon()
//you could wrap prefix with custom class - however it's just a map container
def prefix=[
    method: {'Called from Prefix'}
]


assert w.method()=='Called from Weapon'

w.prefix = prefix
assert w.method()=='Called from Prefix'

w.prefix = null
assert w.method()=='Called from Weapon'
class Weapon {
    void setPrefix(Map m){
        this.metaClass=null                     //reset to default class behavior
        if(m)m.each{k,v-> this.metaClass[k]=v}  //redefine methods/properties from map
    }

    String method() {
        'Called from Weapon'
    }
}

def w = new Weapon()
//you could wrap prefix with custom class - however it's just a map container
def prefix=[
    method: {'Called from Prefix'}
]


assert w.method()=='Called from Weapon'

w.prefix = prefix
assert w.method()=='Called from Prefix'

w.prefix = null
assert w.method()=='Called from Weapon'