有没有一种 "clean" 方法可以将 Java 中对象的所有属性值替换为来自同一类型的另一个对象的值?

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

先简单解释一下。 假设我们有一个方法,它将一个 Student 对象和一个 Id 作为属性,并使用传递的对象中包含的信息更新具有给定 Id 的学生。到目前为止,我找到的最简单的解决方案如下:

  1. 我们搜索我们的知识库寻找我们想要更新的学生。
  2. 如果我们找到它,我们就会使用 setter 来设置新值。
    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());
    }

所以,干得好,我们可以回家了,对吧? 好吧,现在假设我们的项目增长了很多,我们必须向 Student class 添加 20 个以上的属性。突然之间,我们必须手动更新此方法以及执行类似操作的任何其他方法。不好。此外,我们刚刚创建了一些不好的样板代码。

我真正想要的是一种无需考虑我们的 Student class 有多少属性或这些属性是什么的方法,即使它们本身是枚举、数组甚至其他对象。我想要的是这样的:

studentToBeUpdated.replace(newStudent);

这可能吗还是我一厢情愿?下一个最好的事情可能是创建一个可以自己完成所有这些的方法,但它仍然不是一个完美的解决方案,因为每次 class 属性更改时也必须对其进行编辑。

传统的方式,比较啰嗦,但是我们可以放心复制属性。

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

其他方式,使用反射来复制值... 但是有时我们会遇到一些奇怪的问题......对于复杂的对象,例如 Map/List 和其他结构。 使用时请小心... 这是我在我的项目中使用的当前签名。

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

下面是一些处理反射的基本方法。我使用了 spring 中内置的 ReflectionUtils,您可以编写一个或使用其他库...

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

对于两个 Java 对象之间的映射,您可以使用 MapStruct 之类的东西,或者您可以编写自己的映射器,本质上是将样板代码提取到单独的 class/service.

如果您决定创建自己的映射器,您可以使用 reflection 或者您可以使用 setter 方法或构建器模式来完成(Lombok 的 @Builder 也可以帮助避免编写构建器模式的样板文件)。

...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.

当然,您的实体以及您的数据库的数量可能会增加 fields/columns,但如果您在不同的 类 中提取所有映射和转换,这通常不会有什么大不了的].在大多数情况下,无论如何您都必须更新字段的子集。