如何用另一个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'}