18

オブジェクト プロパティを別のオブジェクトに一般的な方法でコピーしたい(プロパティがターゲット オブジェクトに存在する場合は、ソース オブジェクトからコピーします)。

私のコードは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

4 に答える 4

32

最良かつ明確な方法は、 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)

:読みやすくしたい場合は、カテゴリを使用できます

use(InvokerHelper) {
    adminUser.setProperties(user.properties) 
}
于 2014-05-10T16:43:20.347 に答える
31

あなたのソリューションは非常に優れており、正しい方向に進んでいると思います。少なくとも私はそれが非常に理解できると思います。

そのソリューションのより簡潔なバージョンは...

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

基本的に、最初に 2 つのオブジェクト間の共通のプロパティを計算し、次にそれらをコピーします。それも機能しますが、最初のものはより簡単で理解しやすいと思います:)

少ないほうが多い場合もあります。

于 2012-01-31T00:47:14.357 に答える
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 ] ] } )
  }
}
于 2012-01-31T08:53:57.347 に答える
1

ソース/ターゲット クラスが異なるタイプであっても、Spring BeanUtils.copyProperties は機能します。http://docs.spring.io/autorepo/docs/spring/3.2.3.RELEASE/javadoc-api/org/springframework/beans/BeanUtils.html

于 2015-04-22T09:42:09.007 に答える