Copiar todos los valores de los campos de una clase a otra mediante reflexión


Tengo una clase que es básicamente una copia de otra clase.

public class A {
  int a;
  String b;
}

public class CopyA {
  int a;
  String b;
}

Lo que estoy haciendo es poner valores de la clase A en CopyA antes de enviar CopyA a través de una llamada de servicio web. Ahora me gustaría crear un método de reflexión que básicamente copie todos los campos que son idénticos (por nombre y tipo) de la clase A a la clase CopyA.

¿Cómo puedo hacer esto?

Esto es lo que tengo hasta ahora, pero no funciona del todo. Creo que el problema aquí es que estoy tratando de establecer un campo en el campo que estoy recorriendo.

private <T extends Object, Y extends Object> void copyFields(T from, Y too) {

    Class<? extends Object> fromClass = from.getClass();
    Field[] fromFields = fromClass.getDeclaredFields();

    Class<? extends Object> tooClass = too.getClass();
    Field[] tooFields = tooClass.getDeclaredFields();

    if (fromFields != null && tooFields != null) {
        for (Field tooF : tooFields) {
            logger.debug("toofield name #0 and type #1", tooF.getName(), tooF.getType().toString());
            try {
                // Check if that fields exists in the other method
                Field fromF = fromClass.getDeclaredField(tooF.getName());
                if (fromF.getType().equals(tooF.getType())) {
                    tooF.set(tooF, fromF);
                }
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

Estoy seguro de que debe haber alguien que ya haya hecho esto de alguna manera

Author: Joe, 2009-11-03

15 answers

Si no le importa usar una biblioteca de terceros, BeanUtils de Apache Commons manejará esto con bastante facilidad, usando copyProperties(Object, Object).

 71
Author: Greg Case,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-31 16:52:44

¿Por qué no usas la biblioteca gson https://github.com/google/gson

Usted acaba de convertir la Clase A a cadena json. Luego convierte jsonString a tu subClase (CopyA).usando el siguiente código:

Gson gson= new Gson();
String tmp = gson.toJson(a);
CopyA myObject = gson.fromJson(tmp,CopyA.class);
 15
Author: Eric Ho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-09 05:31:24

BeanUtils solo copiará los campos públicos y es un poco lento. En su lugar, vaya con los métodos getter y setter.

public Object loadData (RideHotelsService object_a) throws Exception{

        Method[] gettersAndSetters = object_a.getClass().getMethods();

        for (int i = 0; i < gettersAndSetters.length; i++) {
                String methodName = gettersAndSetters[i].getName();
                try{
                  if(methodName.startsWith("get")){
                     this.getClass().getMethod(methodName.replaceFirst("get", "set") , gettersAndSetters[i].getReturnType() ).invoke(this, gettersAndSetters[i].invoke(object_a, null));
                        }else if(methodName.startsWith("is") ){
                            this.getClass().getMethod(methodName.replaceFirst("is", "set") ,  gettersAndSetters[i].getReturnType()  ).invoke(this, gettersAndSetters[i].invoke(object_a, null));
                        }

                }catch (NoSuchMethodException e) {
                    // TODO: handle exception
                }catch (IllegalArgumentException e) {
                    // TODO: handle exception
                }

        }

        return null;
    }
 7
Author: Supun Sameera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-12 09:29:55

El primer argumento para tooF.set() debe ser el objeto de destino (too), no el campo, y el segundo argumento debe ser el valor , no el campo del que proviene el valor. (Para obtener el valor, debe llamar a fromF.get() again de nuevo pasando un objeto de destino, en este caso from.)

La mayor parte de la API de reflexión funciona de esta manera. Obtiene Field objetos, Method objetos, y así sucesivamente de la clase, no de una instancia, por lo que para usarlos (excepto para la estática) generalmente necesita pasarlos un instancia.

 4
Author: David Moles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-03 15:14:57

Topadora

ACTUALIZACIÓN 19 Nov 2012: Ahora hay un nuevo proyecto ModelMapper también.

 4
Author: Ruben Bartelink,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-19 11:44:29
  1. Sin usar BeanUtils o Apache Commons

  2. public static <T1 extends Object, T2 extends Object>  void copy(T1     
    origEntity, T2 destEntity) throws IllegalAccessException, NoSuchFieldException {
        Field[] fields = origEntity.getClass().getDeclaredFields();
        for (Field field : fields){
            origFields.set(destEntity, field.get(origEntity));
         }
    }
    
 4
Author: Darkhan Iskakov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-19 13:36:27

Creo que puedes probar dozer. Tiene un buen soporte para la conversión de frijol a frijol. También es fácil de usar. Puede inyectarlo en su aplicación de resorte o agregar el frasco en la ruta de clase y listo.

Para un ejemplo de su caso :

 DozerMapper mapper = new DozerMapper();
A a= new A();
CopyA copyA = new CopyA();
a.set... // set fields of a.
mapper.map(a,copyOfA); // will copy all fields from a to copyA
 3
Author: Priyank Doshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-17 10:40:42

Mi solución:

public static <T > void copyAllFields(T to, T from) {
        Class<T> clazz = (Class<T>) from.getClass();
        // OR:
        // Class<T> clazz = (Class<T>) to.getClass();
        List<Field> fields = getAllModelFields(clazz);

        if (fields != null) {
            for (Field field : fields) {
                try {
                    field.setAccessible(true);
                    field.set(to,field.get(from));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}
 3
Author: Mohsen Kashi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-20 14:10:01

Aquí hay una solución de trabajo y probada. Puede controlar la profundidad de la asignación en la jerarquía de clases.

public class FieldMapper {

    public static void copy(Object from, Object to) throws Exception {
        FieldMapper.copy(from, to, Object.class);
    }

    public static void copy(Object from, Object to, Class depth) throws Exception {
        Class fromClass = from.getClass();
        Class toClass = to.getClass();
        List<Field> fromFields = collectFields(fromClass, depth);
        List<Field> toFields = collectFields(toClass, depth);
        Field target;
        for (Field source : fromFields) {
            if ((target = findAndRemove(source, toFields)) != null) {
                target.set(to, source.get(from));
            }
        }
    }

    private static List<Field> collectFields(Class c, Class depth) {
        List<Field> accessibleFields = new ArrayList<>();
        do {
            int modifiers;
            for (Field field : c.getDeclaredFields()) {
                modifiers = field.getModifiers();
                if (!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
                    accessibleFields.add(field);
                }
            }
            c = c.getSuperclass();
        } while (c != null && c != depth);
        return accessibleFields;
    }

    private static Field findAndRemove(Field field, List<Field> fields) {
        Field actual;
        for (Iterator<Field> i = fields.iterator(); i.hasNext();) {
            actual = i.next();
            if (field.getName().equals(actual.getName())
                && field.getType().equals(actual.getType())) {
                i.remove();
                return actual;
            }
        }
        return null;
    }
}
 3
Author: JHead,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-30 08:39:20

Sí, o los BeanUtils de Apache Jakarta.

 1
Author: Shaun F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-03 15:07:38

Orika es un framework de mapeo de frijol simple y más rápido porque lo hace a través de la generación de código de bytes. Hace asignaciones anidadas y asignaciones con diferentes nombres. Para más detalles, por favor marque aquí La asignación de muestras puede parecer compleja, pero para escenarios complejos sería simple.

MapperFactory factory = new DefaultMapperFactory.Builder().build();
mapperFactory.registerClassMap(mapperFactory.classMap(Book.class,BookDto.class).byDefault().toClassMap());
MapperFacade mapper = factory.getMapperFacade();
BookDto bookDto = mapperFacade.map(book, BookDto.class);
 1
Author: Nagappan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-30 03:12:55

Si tienes spring en las dependencias también puedes usar org.springframework.frijol.BeanUtils .

Https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html

 1
Author: db80,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-10 11:15:12

Spring tiene un método incorporado BeanUtils.copyProperties. Pero no funciona con clases sin getters / setters. La serialización/deserialización JSON puede ser otra opción para copiar campos. Jackson se puede utilizar para este propósito. Si está utilizando Spring En la mayoría de los casos, Jackson ya está en su lista de dependencias.

ObjectMapper mapper     = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Clazz        copyObject = mapper.readValue(mapper.writeValueAsString(sourceObject), Clazz.class);
 1
Author: Fırat KÜÇÜK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-28 20:46:09

Este es un post tardío, pero aún puede ser efectivo para la gente en el futuro.

Spring proporciona una utilidad BeanUtils.copyProperties(srcObj, tarObj) que copia valores del objeto de origen al objeto de destino cuando los nombres de las variables miembro de ambas clases son los mismos.

Si hay una conversión de fecha, (por ejemplo, Cadena a Fecha) 'null' se copiaría al objeto de destino. Podemos entonces, establecer explícitamente los valores de la fecha como sea necesario.

El BeanUtils de Apache Common lanza un error cuando hay un desajuste de tipos de datos (esp. conversión a y a partir de la fecha)

Espero que esto ayude!

 1
Author: Nicholas K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-06 14:04:38

No quería agregar dependencia a otro archivo JAR debido a esto, así que escribí algo que se adaptara a mis necesidades. Sigo la convención de fjorm https://code.google.com/p/fjorm / lo que significa que mis campos generalmente accesibles son públicos y que no me molesto en escribir setters y getters. (en mi opinión, el código es más fácil de administrar y más legible en realidad)

Así que escribí algo (en realidad no es muy difícil) que se adapte a mis necesidades (asume que la clase tiene public constructor sin args) y podría ser extraído en la clase de utilidad

  public Effect copyUsingReflection() {
    Constructor constructorToUse = null;
    for (Constructor constructor : this.getClass().getConstructors()) {
      if (constructor.getParameterTypes().length == 0) {
        constructorToUse = constructor;
        constructorToUse.setAccessible(true);
      }
    }
    if (constructorToUse != null) {
      try {
        Effect copyOfEffect = (Effect) constructorToUse.newInstance();
        for (Field field : this.getClass().getFields()) {
          try {
            Object valueToCopy = field.get(this);
            //if it has field of the same type (Effect in this case), call the method to copy it recursively
            if (valueToCopy instanceof Effect) {
              valueToCopy = ((Effect) valueToCopy).copyUsingReflection();
            }
            //TODO add here other special types of fields, like Maps, Lists, etc.
            field.set(copyOfEffect, valueToCopy);
          } catch (IllegalArgumentException | IllegalAccessException ex) {
            Logger.getLogger(Effect.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
        return copyOfEffect;
      } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
        Logger.getLogger(Effect.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
    return null;
  }
 0
Author: Mladen Adamovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-04 18:28:07