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

jucafojl  于 2021-06-29  发布在  Java
关注(0)|答案(1)|浏览(311)

假设以下类别:

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

现在,问题来了:我不想手动编写这个方法,因为它在添加新属性时弹性会降低。在这种情况下,我可能会忘记将其添加到update方法中。
解决方案不需要直接赋值,事实上我更喜欢它调用setter方法。
我也可以使用任何第三方框架,比如lombok。我在找像 @RequiredArgsConstructor ,但是我需要更新新对象,而不是创建新对象。
有点像 @RequiredArgsSetter 或者 Object.updateInto(Object1 o, Object2 o) 方法,但它不应创建新对象,而应简单地更新现有对象的所有字段。
加分,如果有可能对设置中应包含或排除的字段进行注解。

vvppvyoh

vvppvyoh1#

我发现你的问题很有趣,决定试一试。下面是一个使用反射的解决方案。它查找按名称和类型匹配且未被注解排除的字段,然后设置任何匹配字段的值。
免责声明:我还没有彻底测试这个,只是轻轻地。它可能需要一些工作。它也不使用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。最后一个被排除在外,因为名称不匹配。

相关问题