class groovy properties property-list expandometaclass

class - Copie las propiedades de la clase Groovy



properties property-list (4)

Quiero copiar propiedades de objetos a otro objeto de forma genérica (si existe una propiedad en el objeto de destino, la copio del objeto de origen).

Mi código funciona bien usando ExpandoMetaClass , pero no me gusta la solución. ¿Hay alguna otra forma de hacer esto?

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


Creo que tu solución es bastante buena y está en el camino correcto. Al menos, me resulta bastante comprensible.

Una versión más sucinta de esa solución podría ser ...

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

... pero no es fundamentalmente diferente. Estoy iterando sobre las propiedades de origen para poder usar los valores para asignar al objetivo :). Sin embargo, puede ser menos robusto que tu solución original, ya que creo que se rompería si el objeto objetivo define un getAt(String) .

Si quieres ser elegante, puedes hacer algo como esto:

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

Básicamente, primero calcula las propiedades comunes entre los dos objetos y luego los copia. También funciona, pero creo que el primero es más sencillo y más fácil de entender :)

A veces menos es más.


Otra forma es hacer:

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

Que obtiene las propiedades comunes (que no son campos sintéticos) y luego las asigna al objetivo

También podría (utilizando este método) hacer algo como:

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

Así que le pasas al método un objeto para copiar las propiedades y la clase del objeto devuelto. El método luego crea una nueva instancia de esta clase (suponiendo que es un constructor sin args), establece las propiedades y las devuelve.

Editar 2

Suponiendo que se trata de clases Groovy, puede invocar el constructor del Map y establecer todas las propiedades comunes de esta manera:

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


Creo que la mejor y más clara manera es usar el método InvokerHelper.setProperties

Ejemplo:

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"

Salida:

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

Nota : Si quieres más legibilidad, puedes usar la categoría

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