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

lymnna71  于 2021-07-05  发布在  Java
关注(0)|答案(18)|浏览(442)

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

public class A {
  int a;
  String b;
}

public class CopyA {
  int a;
  String b;
}

我所做的就是把课堂上的价值观 A 进入 CopyA 发送前 CopyA 通过网络服务呼叫。现在我想创建一个反射方法,基本上从类中复制所有相同(按名称和类型)的字段 A 上课 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();
            }

        }
    }

我相信一定有人已经做了这件事

fhg3lkii

fhg3lkii1#

我想你可以试试推土机。它对bean到bean的转换有很好的支持。它也很容易使用。您可以将它注入到spring应用程序中,也可以在类路径中添加jar并完成它。
举个例子:

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
cpjpxq1n

cpjpxq1n2#

我在kotlin中解决了上述问题,这对我的android应用程序开发非常有用:

object FieldMapper {

fun <T:Any> copy(to: T, from: T) {
    try {
        val fromClass = from.javaClass

        val fromFields = getAllFields(fromClass)

        fromFields?.let {
            for (field in fromFields) {
                try {
                    field.isAccessible = true
                    field.set(to, field.get(from))
                } catch (e: IllegalAccessException) {
                    e.printStackTrace()
                }

            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }

}

private fun getAllFields(paramClass: Class<*>): List<Field> {

    var theClass:Class<*>? = paramClass
    val fields = ArrayList<Field>()
    try {
        while (theClass != null) {
            Collections.addAll(fields, *theClass?.declaredFields)
            theClass = theClass?.superclass
        }
    }catch (e:Exception){
        e.printStackTrace()
    }

    return fields
}

}

lskq00tm

lskq00tm3#

如果您不介意使用第三方库,apachecommons中的beanutils将使用 copyProperties(Object, Object) .

fhg3lkii

fhg3lkii4#

不使用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));
     }
}
tmb3ates

tmb3ates5#

public static <T> void copyAvalableFields(@NotNull T source, @NotNull T target) throws IllegalAccessException {
    Field[] fields = source.getClass().getDeclaredFields();
    for (Field field : fields) {
        if (!Modifier.isStatic(field.getModifiers())
                && !Modifier.isFinal(field.getModifiers())) {
            field.set(target, field.get(source));
        }
    }
}

我们阅读全班所有的领域。从结果中筛选非静态和非最终字段。但访问非公共字段时可能会出错。例如,如果此函数在同一类中,并且要复制的类不包含公共字段,则会发生访问错误。解决方案可能是将此函数放在同一个包中,或者将访问权限更改为public,或者将此代码中的内容更改为loop call field.setaccessible(true);是什么使这些字段可用

watbbzwu

watbbzwu6#

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

np8igboo7#

因为这个原因,我不想给另一个jar文件添加依赖项,所以写了一些适合我需要的东西。我遵循峡湾的惯例https://code.google.com/p/fjorm/ 这意味着我的一般可访问字段是公共的,我不必费心编写setter和getter(在我看来,代码更易于管理,实际上更具可读性)
所以我写了一些适合我需要的东西(实际上并不难)(假设类有没有参数的公共构造函数),并且可以将它提取到实用类中

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

44u64gxh8#

第一个论点 tooF.set() 应该是目标对象( too ),而不是字段,第二个参数应该是值,而不是值来自的字段(要获得价值,你需要打电话 fromF.get() --在本例中,再次传入目标对象 from .)
大多数反射api都是这样工作的。你得到了吗 Field 物体, Method 对象等,所以要使用它们(除了静态),通常需要向它们传递一个示例。

qxsslcnc

qxsslcnc9#

public <T1 extends Object, T2 extends Object> void copy(T1 origEntity, T2 destEntity) {
        DozerBeanMapper mapper = new DozerBeanMapper();
        mapper.map(origEntity,destEntity);
    }

 <dependency>
            <groupId>net.sf.dozer</groupId>
            <artifactId>dozer</artifactId>
            <version>5.4.0</version>
        </dependency>
voj3qocg

voj3qocg10#

如果依赖项中有spring,那么也可以使用org.springframework.beans.beanutils。
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/beanutils.html

4dbbbstv

4dbbbstv11#

推土机
2012年11月19日更新:现在还有一个新的modelmapper项目。

axr492tv

axr492tv12#

我的解决方案:

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

pepwfjgg13#

这是一个工作和测试的解决方案。可以控制类层次结构中Map的深度。

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

yduiuuwa14#

这是一个迟到的职位,但仍然可以有效的人在未来。
spring提供了一个实用程序 BeanUtils.copyProperties(srcObj, tarObj) 当两个类的成员变量的名称相同时,将值从源对象复制到目标对象。
如果存在日期转换(例如,字符串到日期),则“null”将复制到目标对象。然后,我们可以根据需要显式地设置日期的值。
小海狸 Apache Common 当数据类型不匹配时抛出错误(特别是转换为和转换自日期)
希望这有帮助!

bogh5gae

bogh5gae15#

你为什么不用gson图书馆https://github.com/google/gson
您只需将类a转换为json字符串。然后将jsonstring转换为子类(copya)

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

相关问题