通过reflection将所有从一个类中的字段的值复制到另一个类中
我有一个class,基本上是另一class的副本。
public class A { int a; String b; } public class CopyA { int a; String b; }
我正在做的是在通过web服务调用发送CopyA
之前将A
类A
值放入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; }
-
不使用BeanUtils或Apache Commons
-
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);