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

She*_*ari 73 java reflection

我有一个基本上是另一个类的副本的类.

public class A {
  int a;
  String b;
}

public class CopyA {
  int a;
  String b;
}
Run Code Online (Sandbox Code Playgroud)

我正在做的是在通过webservice调用发送之前将类中的值A放入.现在我想创建一个反射方法,它基本上将所有相同(按名称和类型)的字段从一个类复制到另一个类.CopyACopyAACopyA

我怎样才能做到这一点?

这是我到目前为止所做的,但它并不常用.我认为这里的问题是我试图在我循环的字段上设置一个字段.

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

        }
    }
Run Code Online (Sandbox Code Playgroud)

我确信必定有人已经以某种方式做过这件事

Gre*_*ase 84

如果您不介意使用第三方库,Apache Commons的BeanUtils可以很容易地使用它来处理这个问题copyProperties(Object, Object).

  • 显然,BeanUtils不适用于null Date字段.如果您遇到问题,请使用Apache PropertyUtils:http://www.mail-archive.com/user@commons.apache.org/msg02246.html (12认同)
  • 这显然不适用于没有吸气剂和固定剂的私人田地.任何直接使用字段而不是属性的解决方案? (8认同)

Eri*_* Ho 17

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

你只需将A类转换为json字符串.然后将jsonString转换为subClass(CopyA).使用下面的代码:

Gson gson= new Gson();
String tmp = gson.toJson(a);
CopyA myObject = gson.fromJson(tmp,CopyA.class);
Run Code Online (Sandbox Code Playgroud)


Sup*_*era 8

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;
    }
Run Code Online (Sandbox Code Playgroud)

  • 这只有在两个bean具有相同数据类型的字段时才有效. (2认同)

JHe*_*ead 8

这是一个有效且经过测试的解决方案。您可以控制类层次结构中的映射深度。

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*les 5

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

大多数反射 API 都是这样工作的。您从类而不是实例中获取Field对象、对象等,因此要使用它们(静态除外),通常需要向它们传递一个实例。Method


Moh*_*shi 5

我的解决方案:

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;
}
Run Code Online (Sandbox Code Playgroud)


Fır*_*ÇÜK 5

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);
Run Code Online (Sandbox Code Playgroud)


Nic*_*s K 5

这是一个迟到的帖子,但对未来的人仍然有效。

Spring 提供了一个实用程序BeanUtils.copyProperties(srcObj, tarObj),当两个类的成员变量的名称相同时,该实用程序将值从源对象复制到目标对象。

如果有日期转换(例如,字符串到日期)'null' 将被复制到目标对象。然后,我们可以根据需要明确设置日期的值。

Apache Common当数据类型不匹配时,BeanUtils from会抛出错误(特别是与 Date 的转换)

希望这可以帮助!