如何用另一个java对象的值更新一个java对象的所有字段?

如何用另一个java对象的值更新一个java对象的所有字段?,java,lombok,Java,Lombok,假设以下类别: public class TestClass { String attr1; String attr2; String attr3; } 客户端代码如下: final TestClass testClassA = new TestClass(); testClassA.attr1 = "1"; testClassA.attr1 = "2"; testClassA.attr1 = "3"; fi

假设以下类别:

public class TestClass {
    String attr1;
    String attr2;
    String attr3;
}
客户端代码如下:

final TestClass testClassA = new TestClass();
testClassA.attr1 = "1";
testClassA.attr1 = "2";
testClassA.attr1 = "3";

final TestClass testClassB = new TestClass();
我想找到用
testClassA
的所有值更新
testClassB
的方法

testClassB.updateAll(testClassA)
其中一种解决办法是:

public void updateAll(TestClass testClass) {
    this.attr1 = testClass.attr1;
    this.attr2 = testClass.attr2;
    this.attr3 = testClass.attr3;
}
现在,问题来了:我不想手动编写这个方法,因为它在添加新属性时弹性会降低。在这种情况下,我可能会忘记将其添加到update方法中

这个解决方案不需要直接赋值,事实上我更喜欢它调用setter方法

我也可以使用任何第三方框架,比如Lombok。我正在寻找类似于
@RequiredArgsConstructor
的东西,但是我需要更新而不是创建新对象

因此,类似于
@requiredArgsetter
Object.updateInto(object1o,object2o)
方法,但同样,它不应该创建新对象,而只是更新现有对象的所有字段


如果可以对设置中应包含或排除的字段进行注释,则可获得额外积分。

我发现您的问题很有趣,决定尝试一下。下面是一个使用反射的解决方案。它查找按名称和类型匹配且未被注释排除的字段,然后设置任何匹配字段的值

免责声明:我没有彻底测试过这个,只是测试得很轻。它可能需要一些工作。它也不使用setter方法,而是只设置字段值

属性复制方法:

public class AttrCopy {

    public void copyAttributes(Object from, Object to) throws IllegalAccessException {
        Map<String, Field> toFieldNameMap = new HashMap<>();
        for(Field f : to.getClass().getDeclaredFields()) {
            toFieldNameMap.put(f.getName(), f);
        }
        for(Field f : from.getClass().getDeclaredFields()) {
            Field ff = toFieldNameMap.get(f.getName());
            f.setAccessible(true);
            boolean include = f.getDeclaredAnnotation(AttrCopyExclude.class) == null;
            if(include && ff != null && ff.getType().equals(f.getType())) {
                ff.setAccessible(true);
                ff.set(to, f.get(from));
            }
        }
    }
}
测试类别:

public class ClassA {
    private String attribute1;
    private int attribute2;
    private int attribute3;
    private String attribute4;
    private String attribute5;

    // toString()
}

public class ClassB {
    private String attribute1;
    private int attribute2;
    private String attribute3;
    @AttrCopyExclude
    private String attribute4;
    private String attribute6;

    // toString()
}
测试代码:

public class Tester {
    public static void main(String[] args) throws IllegalAccessException {
        ClassA classA = new ClassA("aaa", 123, 456, "ddd", "eee");
        ClassB classB = new ClassB("111", 789, "333", "444", "555");

        System.out.println("Before");
        System.out.println(classA);
        System.out.println(classB);

        new AttrCopy().copyAttributes(classB, classA);

        System.out.println("After copy A -> B");
        System.out.println(classA);
        System.out.println(classB);
    }
}
测试输出:

Before
ClassA{attribute1='aaa', attribute2=123, attribute3=456, attribute4='ddd', attribute5='eee'}
ClassB{attribute1='111', attribute2=789, attribute3='333', attribute4='444', attribute6='555'}
After copy B -> A
ClassA{attribute1='111', attribute2=789, attribute3=456, attribute4='ddd', attribute5='eee'}
ClassB{attribute1='111', attribute2=789, attribute3='333', attribute4='444', attribute6='555'}

将复制属性1和2。3被排除在外,因为类型不匹配。注释排除了4。最后一个被排除在外,因为名称不匹配。

为什么不克隆对象呢?正如我所说,我希望它更新现有对象,而不是创建新对象。你可以通过反射来实现这一点……你可以查询相关类的元数据,找出它包含的属性,并使用该数据以编程方式复制每个变量。我不知道现有的图书馆会这样做。我建议您的对象将其字段存储在单个贴图值中。您可以编写引用映射的getter和setter,这样您就不会知道其中的区别。但是,当复制对象时,您可以只复制映射,然后得到其中存储的任何值。对,这是我也想到过的解决方案,但我想知道JDK或其他库中是否已经存在这样做的任何内容。另一种方法是为这样的setter创建字节码,就像Lombok对@RequiredArgsConstructor所做的那样……但是我也不知道有哪种框架有这样的注释。谢谢你的努力!这个解决方案看起来确实可以解决问题,尽管我们可能还没有考虑到一些边缘情况;-)。我不会将其标记为已接受,因为它需要手动反射代码(我宁愿远离手动反射代码),但如果没有其他解决方案,我将在周一为我们的代码试用它。谢谢你@schrobe Fair:)我几乎想将它构建到一个lib中,并将其推到Maven central,但随后我需要对其进行全面测试,这感觉工作量太大了D
Before
ClassA{attribute1='aaa', attribute2=123, attribute3=456, attribute4='ddd', attribute5='eee'}
ClassB{attribute1='111', attribute2=789, attribute3='333', attribute4='444', attribute6='555'}
After copy B -> A
ClassA{attribute1='111', attribute2=789, attribute3=456, attribute4='ddd', attribute5='eee'}
ClassB{attribute1='111', attribute2=789, attribute3='333', attribute4='444', attribute6='555'}