Class 复制Groovy类属性

Class 复制Groovy类属性,class,groovy,properties,property-list,expandometaclass,Class,Groovy,Properties,Property List,Expandometaclass,我想以一般方式将对象属性复制到另一个对象(如果目标对象上存在属性,我将从源对象复制该属性) 我的代码可以很好地使用,但我不喜欢这个解决方案。还有其他方法吗 class User { String name = 'Arturo' String city = 'Madrid' Integer age = 27 } class AdminUser { String name String city Integer age } def copyProp

我想以一般方式将对象属性复制到另一个对象(如果目标对象上存在属性,我将从源对象复制该属性)

我的代码可以很好地使用,但我不喜欢这个解决方案。还有其他方法吗

class User {
    String name = 'Arturo'
    String city = 'Madrid'
    Integer age = 27
}

class AdminUser {
    String name
    String city
    Integer age
}

def copyProperties(source, target) {
    target.properties.each { key, value ->
        if (source.metaClass.hasProperty(source, key) && key != 'class' && key != 'metaClass') {
            target.setProperty(key, source.metaClass.getProperty(source, key))
        }
    }
}

def (user, adminUser) = [new User(), new AdminUser()]
assert adminUser.name == null
assert adminUser.city == null
assert adminUser.age == null

copyProperties(user, adminUser)
assert adminUser.name == 'Arturo'
assert adminUser.city == 'Madrid'
assert adminUser.age == 27

我认为你的解决方案很好,而且是正确的。至少我觉得这是可以理解的

更简洁的解决方案可能是

def copyProperties(source, target) {
    source.properties.each { key, value ->
        if (target.hasProperty(key) && !(key in ['class', 'metaClass'])) 
            target[key] = value
    }
}
。。。但这并没有根本的不同。我正在迭代源属性,以便使用这些值分配给目标:)。但是,它可能不如您原来的解决方案那么健壮,因为我认为如果目标对象定义了
getAt(String)
方法,它就会崩溃

如果你想变得花哨,你可以这样做:

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 copyProperties( source, target ) {
  [source,target]*.getClass().declaredFields*.grep { !it.synthetic }.name.with { a, b ->
    a.intersect( b ).each {
      target."$it" = source."$it"
    }
  }
}
它获取公共属性(不是合成字段),然后将它们指定给目标


您还可以(使用此方法)执行以下操作:

def user = new User()

def propCopy( src, clazz ) {
  [src.getClass(), clazz].declaredFields*.grep { !it.synthetic }.name.with { a, b ->
    clazz.newInstance().with { tgt ->
      a.intersect( b ).each {
        tgt[ it ] = src[ it ]
      }
      tgt
    }
  }
}


def admin = propCopy( user, AdminUser )
assert admin.name == 'Arturo'
assert admin.city == 'Madrid'
assert admin.age == 27
因此,向方法传递一个要从中复制属性的对象,以及返回对象的类。然后,该方法创建该类的新实例(假设没有参数构造函数),设置属性并返回它


编辑2 假设这些是Groovy类,您可以调用
Map
构造函数并设置所有公共属性,如下所示:

def propCopy( src, clazz ) {
  [src.getClass(), clazz].declaredFields*.grep { !it.synthetic }.name.with { a, b ->
    clazz.metaClass.invokeConstructor( a.intersect( b ).collectEntries { [ (it):src[ it ] ] } )
  }
}
我认为最好且清晰的方法是使用InvokerHelper.setProperties方法

例如:

import groovy.transform.ToString
import org.codehaus.groovy.runtime.InvokerHelper

@ToString
class User {
    String name = 'Arturo'
    String city = 'Madrid'
    Integer age = 27
}

@ToString
class AdminUser {
    String name
    String city
    Integer age
}

def user = new User()
def adminUser = new AdminUser()

println "before: $user $adminUser"
InvokerHelper.setProperties(adminUser, user.properties)
println "after : $user $adminUser"
输出:

before: User(Arturo, Madrid, 27) AdminUser(null, null, null)
after : User(Arturo, Madrid, 27) AdminUser(Arturo, Madrid, 27)
注意:如果您想要更高的可读性,可以使用category

use(InvokerHelper) {
    adminUser.setProperties(user.properties) 
}

即使源/目标类的类型不同,Spring BeanUtils.copyProperties也可以工作

您可以始终使用BeanUtils。@DaveNewton不确定BeanUtils是否可以用作源和目标,因为它们是不同的类…相关:使用AutoClone注释如何?除了映射构造函数之外,还可以将属性作为映射分配给具有
MetaClass.setProperties
的现有实例:对于单行程序爱好者,我认为类似的方法应该有效:
[source,target]*.properties*.keySet().grep{it!=“class”&&it!=“MetaClass”}。每个{target[it]=source[it]}
可能还希望将目标分配置于try catch中,以防无法强制类型在调试时在Idea中工作,但在部署的jar上调用时引发异常:“无方法签名:java.util.ArrayList.keySet()适用于参数类型:()值:[]\n可能的解决方案:toSet(),toSet(),set()(int,java.lang.Object)、set(int,java.lang.Object)、get(int)、get(int)当参数具有名为
properties
的属性或名为
getProperties()的方法时,此操作失败
零参数。这些情况的答案可以在这里找到:感谢InvokerHelper,与此问题斗争了一段时间。我认为这是一个非常好的主意,它在DomainUnitTest案例中工作,然后,在没有太多警告的情况下,它在服务集成测试中失败了!grrr…为什么!好的,原来我是反向的,并且我的测试只是测试两者是否相等。副本将它们都设置为空!