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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1667854/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 18:56:38  来源:igfitidea点击:

Copy all values from fields in one class to another through reflection

javareflection

提问by Shervin Asgari

I have a class which is basically a copy of another class.

我有一个课程,它基本上是另一个课程的副本。

public class A {
  int a;
  String b;
}

public class CopyA {
  int a;
  String b;
}

What I am doing is putting values from class Ainto CopyAbefore sending CopyAthrough a webservice call. Now I would like to create a reflection-method that basically copies all fields that are identical (by name and type) from class Ato class CopyA.

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

How can I do this?

我怎样才能做到这一点?

This is what I have so far, but it doesn't quite work. I think the problem here is that I am trying to set a field on the field I am looping through.

这是我到目前为止所拥有的,但它并不完全有效。我认为这里的问题是我试图在我正在循环的字段上设置一个字段。

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

        }
    }

I am sure there must be someone that has already done this somehow

我相信一定有人已经以某种方式这样做了

采纳答案by Greg Case

If you don't mind using a third party library, BeanUtilsfrom Apache Commons will handle this quite easily, using copyProperties(Object, Object).

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

回答by Ruben Bartelink

Dozer

推土机

UPDATE Nov 19 2012: There's now a new ModelMapper projecttoo.

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

回答by Shaun F

Yeah or the BeanUtils from Apache Jakarta.

是的,或者来自 Apache Jakarta 的 BeanUtils。

回答by David Moles

The first argument to tooF.set()should be the target object (too), not the field, and the second argument should be the value, not the field the value comes from. (To get the value, you need to call fromF.get()-- again passing in a target object, in this case from.)

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

Most of the reflection API works this way. You get Fieldobjects, Methodobjects, and so on from the class, not from an instance, so to use them (except for statics) you generally need to pass them an instance.

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

回答by Supun Sameera

BeanUtils will only copy public fields and is a bit slow. Instead go with getter and setter methods.

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

回答by Priyank Doshi

I think you can try dozer. It has good support for bean to bean conversion. Its also easy to use. You can either inject it into your spring application or add the jar in class path and its done.

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

For an example of your case :

以您的情况为例:

 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

回答by Nagappan

Orika's is simple faster bean mapping framework because it does through byte code generation. It does nested mappings and mappings with different names. For more details, please check hereSample mapping may look complex, but for complex scenarios it would be simple.

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

回答by Mladen Adamovic

I didn't want to add dependency to another JAR file because of this, so wrote something which would suit my needs. I follow the convention of fjorm https://code.google.com/p/fjorm/which means that my generally accessible fields are public and that I don't bother to write setters and getters. (in my opinion code is easier to manage and more readable actually)

由于这个原因,我不想向另一个 JAR 文件添加依赖项,所以写了一些适合我需要的东西。我遵循 fjorm https://code.google.com/p/fjorm/的约定,这意味着我通常可访问的字段是公开的,而且我不会费心编写 setter 和 getter。(在我看来,代码实际上更易于管理且更具可读性)

So I wrote something (it's not actually much difficult) which suits my needs (assumes that the class has public constructor without args) and it could be extracted into utility class

所以我写了一些适合我需要的东西(实际上并不难)(假设该类具有没有 args 的公共构造函数)并且它可以被提取到实用程序类中

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

回答by Darkhan Iskakov

  1. Without using BeanUtils or 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));
         }
    }
    
  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));
         }
    }
    

回答by db80

If you have spring in the dependencies you can also use org.springframework.beans.BeanUtils.

如果您在依赖项中有 spring,您还可以使用org.springframework.beans.BeanUtils

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

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