简体   繁体   中英

Is there a "clean" way to replace all attribute values of an object in Java with values from another object of the same type?

A quick explanation first. Let's say we have a method which takes a Student object and an Id as attributes and updates student with given Id with information contained in passed object. So far the easiest solution I've found is as follows:

  1. We search our repository looking for student that we want to update.
  2. Should we find it we then use setters in order to set the new values.
    public void updateStudent(Long id, Student newStudent) {
        Student studentToBeUpdated = studentRepo
                .findStudentById(id)
                .orElseThrow(() ->
                        new UserNotFoundException("Student with id " + id + " was not found"));
        studentToBeUpdated.setFirstName(newStudent.getFirstName());
        studentToBeUpdated.setLastName(newStudent.getLastName());
        studentToBeUpdated.setEmail(newStudent.getEmail());
    }

So, job well done, we can go home, right? Well, now lets hypothetically say our project grew a lot and we have to add 20 more attributes to our Student class. Suddenly we have to update this method manually as well as any other method that does a similar thing. Not good. Also we just created some boilerplate code which is bad.

What I'm really looking for is a way to do all this without thinking about how many attributes our Student class has or what these attributes are, even if they are themselves enums, arrays or even other objects. What I'd like is something like this:

studentToBeUpdated.replace(newStudent);

Is this possible or is it just my wishful thinking? The next best thing would probably be creating a method that would do all this by myself but it's still not a perfect solution because it would also have to be edited every time class attributes change.

For mapping between two Java objects you can use something like MapStruct or you can write your own mappers, essentially extracting the boilerplate code into a separate class/service.

If you decide to create your own mapper, you can use reflection or you can do it by using setter methods or builder pattern (also Lombok's @Builder can help avoiding to write the boilerplate for builder pattern).

...now lets hypothetically say our project grew a lot and we have to add 20 more attributes to our Student class. Suddenly we have to update this method manually as well as any other method that does a similar thing.

Certainly your entities and consequently your databases can grow in the number of fields/columns, but this in general should not be as big of a deal if you extract all the mappings and transformations in different classes. In most of the cases you will have to update a subset of the fields anyway.

With traditional way, it's quite wordy but we can confident the copy attributes.

public Student copyFrom(Student student)
public void copyFrom(Student student)

Other way, Use reflections to copy the values... But we have few strange problems sometimes... with complex objects such as Map/List and other structures. Please carefully when using... Here is current signature which i used in my projects.

public void copyFrom(Object src, List<String> excludes) {
    final List<String> excFields = excludes;
    ReflectionUtils.doWithFields(src.getClass(), fieldCallback-> {});
    ....
  }

Here is few basic methods to do with reflection. I used ReflectionUtils which is built-in in spring, you can write one or use other library...

Field field= ReflectionUtils.findField(cls, fieldName);
Method m = ReflectionUtils.findMethod(cls, fieldName);
field.set(this,value)

I usally use BeanUtils which is found in springframework

public static void copyBean(Object sourceBean, Object targetBean) {

    try {
        org.springframework.beans.BeanUtils.copyProperties(sourceBean,
                targetBean);
    } catch (Exception e) {
        LOG.trace("exception ocurred in copying bean", e);
    }

}

copyBean(obj1,obg2)

There is another project in apache named commons-beanutils which can do this too.

For properties which have same name, these copies try to do their best to convert properties. But if they fail you should do it manually.

Some times we you can do it like this.

//call this copy to make all same and well formed properties copied
copyBean(obj1,obj2);

//now manually set the rest of properties which have different names or mismatch types
obj2.setFoo (  obj1.getBar() );
....

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM