如何更改 Groovy 中方法委托的顺序?
How can I change the order of method delegation in Groovy?
我的目标是更改 Groovy 中方法委托的顺序,以便我可以动态覆盖 Groovy 对象中的方法。
我有一个名为 Weapon
的 class,定义如下:
class Weapon {
Prefix prefix
String method() {
'Called from Weapon'
}
}
其中 prefix
是此 class 的实例:
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)
}
}
此设计允许 Prefix
在运行时动态添加方法。
特定 所需的行为是 Weapon
调用的任何方法将首先在 prefix
中搜索它(如果它不为空)。如果它不存在,那么 Weapon
将使用其正常行为搜索该方法(如此处所示:https://docs.groovy-lang.org/latest/html/documentation/core-metaprogramming.html#_runtime_metaprogramming)。
这是所需行为的一个简单示例:
Weapon w = new Weapon()
Prefix p = new Prefix()
p.method = { 'Called from Prefix' }
w.prefix = p
assert w.method() == 'Called from Prefix'
w.prefix = null
assert w.method() == 'Called from Weapon'
我对 Groovy 的元编程还很陌生,所以我一点也不了解它的所有功能。起初,我以为我可以通过简单地覆盖 Weapon 中的 invokeMethod
方法来做到这一点,但是 Groovy 最终在 invokeMethod
被调用之前找到了该方法(我假设它是在metaclass 或 class).
我能想到的唯一解决方案是为 Weapon
创建一个自定义元类,它首先检查 Weapon
的前缀。我的实现如下:
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)
}
}
但是,这没有用,来自 Weapon
(有或没有 prefix
)的每个方法调用都返回 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'
我的目标是更改 Groovy 中方法委托的顺序,以便我可以动态覆盖 Groovy 对象中的方法。
我有一个名为 Weapon
的 class,定义如下:
class Weapon {
Prefix prefix
String method() {
'Called from Weapon'
}
}
其中 prefix
是此 class 的实例:
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)
}
}
此设计允许 Prefix
在运行时动态添加方法。
特定 所需的行为是 Weapon
调用的任何方法将首先在 prefix
中搜索它(如果它不为空)。如果它不存在,那么 Weapon
将使用其正常行为搜索该方法(如此处所示:https://docs.groovy-lang.org/latest/html/documentation/core-metaprogramming.html#_runtime_metaprogramming)。
这是所需行为的一个简单示例:
Weapon w = new Weapon()
Prefix p = new Prefix()
p.method = { 'Called from Prefix' }
w.prefix = p
assert w.method() == 'Called from Prefix'
w.prefix = null
assert w.method() == 'Called from Weapon'
我对 Groovy 的元编程还很陌生,所以我一点也不了解它的所有功能。起初,我以为我可以通过简单地覆盖 Weapon 中的 invokeMethod
方法来做到这一点,但是 Groovy 最终在 invokeMethod
被调用之前找到了该方法(我假设它是在metaclass 或 class).
我能想到的唯一解决方案是为 Weapon
创建一个自定义元类,它首先检查 Weapon
的前缀。我的实现如下:
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)
}
}
但是,这没有用,来自 Weapon
(有或没有 prefix
)的每个方法调用都返回 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'