代码是可行的,但是在某些边角情况下可能会出现问题。
为了修复这个问题,请将属性访问properties
替换为方法调用getProperties()
,这可能对您的情况已经足够。要覆盖所有情况,您需要编写特殊情况的代码(请参见底部)。
原始版本的工作示例
def copyProperties(source, target) {
def (sProps, tProps) = [source, target]*.properties*.keySet()
def commonProps = sProps.intersect(tProps) - ['class', 'metaClass']
commonProps.each { target[it] = source[it] }
}
def a = new Expando()
a.foo = "foo"
a.bar = "bar"
def b = new Expando()
b.baz = "baz"
b.bar = "old"
copyProperties(a, b)
println b
引起问题的示例
如果参数有一个名为properties
的属性,当值为List
时,我会得到与您相同的异常:
def c = new Expando()
c.properties = []
c.bar = "bar"
def d = new Expando()
d.baz = "baz"
d.bar = "old"
copyProperties(c, d)
println d
在两种情况下均适用的内容:
def copyProperties(source, target) {
def (sProps, tProps) = [source, target]*.getProperties()*.keySet()
def commonProps = sProps.intersect(tProps) - ['class', 'metaClass']
commonProps.each { target[it] = source[it] }
}
这里我明确调用了getProperties
,而不是直接访问properties
属性。
我们仍然可以打断它
def e = new Object() {
def getProperties() {
return []
}
def bar = "bar"
}
def f = new Expando()
f.baz = "baz"
f.bar = "old"
copyProperties(e, f)
通过显式地使用metaClass,您可以修复最后一个示例中的e
def copyProperties(source, target) {
def (sProps, tProps) = [source, target]*.getMetaClass()*.properties*.name
def commonProps = sProps.intersect(tProps) - ['class', 'metaClass']
commonProps.each { target[it] = source[it] }
}
然而,由于 f
的存在,那将会失败。
处理特殊情况
def getProperties(Expando obj) {
return obj.getProperties().keySet()
}
def getProperties(Object obj) {
return obj.getMetaClass()*.properties*.name
}
def copyProperties(source, target) {
def (sProps, tProps) = [source, target].collect {getProperties(it)}
def commonProps = sProps.intersect(tProps) - ['class', 'metaClass']
commonProps.each { target[it] = source[it] }
}
在这里,我们给需要特别处理的对象提供它们所需的内容 ;)
请注意,这仅适用于使用@CompileDynamic
的Groovy,因为调用哪个getProperties
实现将在运行时进行决策。另一种选择是对所有情况进行instanceof
检查。
copyProperties
的方式有关。我将您的代码与原始线程混合在一起,它能够正常工作。 - Michael Easterproperties
也会返回值(例如昂贵的getter)。 - cfrick