Copy Groovy class properties

后端 未结 4 1099
鱼传尺愫
鱼传尺愫 2020-12-01 21:10

I want to copy object properties to another object in a generic way (if a property exists on target object, I copy it from the source object).

My co

相关标签:
4条回答
  • 2020-12-01 21:33

    I think the best and clear way is to use InvokerHelper.setProperties method

    Example:

    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"
    

    Output:

    before: User(Arturo, Madrid, 27) AdminUser(null, null, null)
    after : User(Arturo, Madrid, 27) AdminUser(Arturo, Madrid, 27)
    

    Note: If you want more readability you can use category

    use(InvokerHelper) {
        adminUser.setProperties(user.properties) 
    }
    
    0 讨论(0)
  • 2020-12-01 21:43

    I think your solution is quite good and is in the right track. At least I find it quite understandable.

    A more succint version of that solution could be...

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

    ... but it's not fundamentally different. I'm iterating over the source properties so I can then use the values to assign to the target :). It may be less robust than your original solution though, as I think it would break if the target object defines a getAt(String) method.

    If you want to get fancy, you might do something like this:

    def copyProperties(source, target) {
        def (sProps, tProps) = [source, target]*.properties*.keySet()
        def commonProps = sProps.intersect(tProps) - ['class', 'metaClass']
        commonProps.each { target[it] = source[it] }
    }
    

    Basically, it first computes the common properties between the two objects and then copies them. It also works, but I think the first one is more straightforward and easier to understand :)

    Sometimes less is more.

    0 讨论(0)
  • 2020-12-01 21:44

    Another way is to do:

    def copyProperties( source, target ) {
      [source,target]*.getClass().declaredFields*.grep { !it.synthetic }.name.with { a, b ->
        a.intersect( b ).each {
          target."$it" = source."$it"
        }
      }
    }
    

    Which gets the common properties (that are not synthetic fields), and then assigns them to the target


    You could also (using this method) do something like:

    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
    

    So you pass the method an object to copy the properties from, and the class of the returned object. The method then creates a new instance of this class (assuming a no-args constructor), sets the properties and returns it.


    Edit 2

    Assuming these are Groovy classes, you can invoke the Map constructor and set all the common properties like so:

    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 ] ] } )
      }
    }
    
    0 讨论(0)
  • 2020-12-01 21:57

    Spring BeanUtils.copyProperties will work even if source/target classes are different types. http://docs.spring.io/autorepo/docs/spring/3.2.3.RELEASE/javadoc-api/org/springframework/beans/BeanUtils.html

    0 讨论(0)
提交回复
热议问题