通过reflection将所有从一个类中的字段的值复制到另一个类中

我有一个class,基本上是另一class的副本。

public class A { int a; String b; } public class CopyA { int a; String b; } 

我正在做的是在通过web服务调用发送CopyA之前将AA值放入CopyA 。 现在我想创build一个reflection方法,它基本上将从A类到class CopyA所有字段(按名称和types) CopyA

我怎样才能做到这一点?

这是我迄今为止,但它不工作。 我认为这里的问题是,我试图在我正在循环的字段上设置一个字段。

 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(); } } } 

我确定肯定有人已经做了这个

如果您不介意使用第三方库,Apache Commons的BeanUtils将使用copyProperties(Object, Object)相当容易地处理这个问题。

BeanUtils只会复制公有字段,速度有点慢。 取而代之的是getter和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; } 

为什么不使用gson库https://github.com/google/gson

您只需将A类转换为jsonstring。 然后将jsonString转换为你的subClass(CopyA),使用下面的代码:

 Gson gson= new Gson(); String tmp = gson.toJson(a); CopyA myObject = gson.fromJson(tmp,CopyA.class); 

tooF.set()的第一个参数应该是目标对象( too ),而不是字段,第二个参数应该是 ,而不是值来自的字段。 (要获得值,你需要调用fromF.get() – 再次传入一个目标对象,在这个例子中是from 。)

大部分的reflectionAPI都是这样工作的。 您从类中获取Field对象, Method对象等,而不是从实例获取,因此要使用它们(除了静态),您通常需要将它们传递给实例。

我想你可以尝试推土机 。 它对bean到bean的转换有很好的支持。 它也很容易使用。 你可以将它注入到你的spring应用程序中,或者将它添加到类path中,然后完成。

有关您的案例的示例:

  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 

推土机

更新2012年11月19日:现在还有一个新的ModelMapper项目 。

我的解决scheme

 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; } 
  1. 不使用BeanUtils或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)); } } 

是的,或者Apache Jakarta的BeanUtils。

如果你在依赖关系中有弹性,你也可以使用org.springframework.beans.BeanUtils

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

Spring有一个内置的BeanUtils.copyProperties方法。 但是它不适用于没有getter / setter的类。 JSON序列化/反序列化可以是复制字段的另一个选项。 jackson可以用于这个目的。 如果你使用Spring在大多数情况下,Jackson已经在你的依赖列表中。

 ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); Clazz copyObject = mapper.readValue(mapper.writeValueAsString(sourceObject), Clazz.class); 

这是一个工作和testing的解决scheme。 您可以控制类层次结构中的映射深度。

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

因为这个,我不想添加依赖到另一个JAR文件,所以写了一些适合我的需求的东西。 我遵循惯例https://code.google.com/p/fjorm/这意味着我通常可访问的字段是公开的,我不打扰编写setters和getters。; (在我看来,代码更容易pipe理,实际上更具可读性)

所以我写了一些符合我的需求的东西(其实并不难)(假定这个类有公共构造函数,没有参数),它可以被提取到实用类

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

Orika的简单快速的bean映射框架,因为它通过字节代码生成。 它使用不同的名称嵌套映射和映射。 有关更多详细信息,请点击此处示例映射可能看起来很复杂,但是对于复杂的场景,它可能很简单。

 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);