首页 文章

复制Groovy类属性

提问于
浏览
14

我想以通用的方式 copy object properties to another object (如果目标对象上存在属性,我从源对象复制它) .

我的代码使用ExpandoMetaClass工作正常,但我不喜欢这个解决方案 . 有没有其他方法可以做到这一点?

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

4 回答

  • 27

    即使源/目标类是不同类型,Spring BeanUtils.copyProperties也能工作 . http://docs.spring.io/autorepo/docs/spring/3.2.3.RELEASE/javadoc-api/org/springframework/beans/BeanUtils.html

  • 0

    我认为您的解决方案非常好,并且处于正确的轨道上 . 至少我觉得这很容易理解 .

    该解决方案的更加简洁的版本可能是......

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

    ...但是's not fundamentally different. I' m迭代了源属性,因此我可以使用这些值来分配给目标:) . 它可能不如原始解决方案强大,因为我认为如果目标对象定义 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] }
    }
    

    基本上,它首先计算两个对象之间的公共属性,然后复制它们 . 它也有效,但我认为第一个更直接,更容易理解:)

    有时少即是多 .

  • 28

    我认为最好和最明确的方法是使用 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)
    

    Note :如果您想要更多可读性,可以使用类别

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

    另一种方法是:

    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 ] ] } )
      }
    }
    

相关问题