首页 > 解决方案 > 如何用另一个 java 对象的值更新一个 java 对象的所有字段?

问题描述

假设以下类:

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

我想找到更新testClassB所有值的方式/方法testClassA

testClassB.updateAll(testClassA)

一种这样的解决方案是:

public void updateAll(TestClass testClass) {
    this.attr1 = testClass.attr1;
    this.attr2 = testClass.attr2;
    this.attr3 = testClass.attr3;
}

现在,事情来了:我希望不必手动编写此方法,以便在添加新属性时降低弹性。在这种情况下,我可能会忘记将其添加到更新方法中。

该解决方案不需要直接分配值,实际上我更喜欢它调用 setter 方法。

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

所以类似 a@RequiredArgsSetterObject.updateInto(Object1 o, Object2 o)方法的东西,但同样,它不应该创建一个新对象,而只是简单地更新现有对象的所有字段。

奖励积分,如果可以以某种方式注释应该包含或排除在设置中的字段。

标签: javalombok

解决方案


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

免责声明:我没有彻底测试过这个,只是轻轻地。它可能需要一些工作。它也不使用 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));
            }
        }
    }
}

排除字段的注释:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AttrCopyExclude {
}

测试类:

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被注释排除。最后一个被排除,因为名称不匹配。


推荐阅读