我有两个相同类型的对象。
Class A {
String a;
List b;
int c;
}
A obj1 = new A();
A obj2 = new A();
obj1 => {a ="hello"; b = null; c = 10}
obj2 => {a = null; b = new ArrayList(); c = default value}
您能否让我知道将这些对象组合为单个对象的最佳方法是什么?
obj3 = {a ="hello"; b = (same arraylist from obj2); c = 10}
您能描述一下这个"合并"对象的外观吗?
如果obj1.a ="George"和obj2.a ="Lucas"在"合并"对象中的obj3.a值应该是什么?
您可以假设对象是互斥的
只要您具有自己的getter和setter的POJO都可以使用。该方法使用update中的非空值更新obj。它在obj上调用setParameter(),并在更新时返回getParameter()的返回值:
public void merge(Object obj, Object update){
if(!obj.getClass().isAssignableFrom(update.getClass())){
return;
}
Method[] methods = obj.getClass().getMethods();
for(Method fromMethod: methods){
if(fromMethod.getDeclaringClass().equals(obj.getClass())
&& fromMethod.getName().startsWith("get")){
String fromName = fromMethod.getName();
String toName = fromName.replace("get","set");
try {
Method toMetod = obj.getClass().getMethod(toName, fromMethod.getReturnType());
Object value = fromMethod.invoke(update, (Object[])null);
if(value != null){
toMetod.invoke(obj, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
感谢您的回答...但是我的对象有很多原始类型,例如int,float e.t.c.它还具有列表作为成员。
没关系。只要两个对象都具有getXX及其对应的setXX,这将起作用。它将仅使用getter和setter复制值
使用obj.getClass()。getDeclaredMethods()跳过getClass / setClass等,仅获取在该类或相应接口中定义的方法。
我对第一个if感到困惑。试图达到什么目的?
它不应该返回对象吗?
@jon函数更新obj参数对象,以便将所有更改保存到obj实例中,而不是返回更新的对象。您可以在函数末尾返回obj,但这不是必需的。
String toName = fromName.replace("get","set");如果我的字段名称为thisisfieldsetted会怎样?
通常,此代码一目了然。必须有一种更好的方式来编写此代码。因此,我是Zons的粉丝,请在下面回答。
也许像
class A {
String a;
List<..> b;
int c;
public void merge(A other) {
this.a = other.a == null ? this.a : other.a;
this.b.addAll(other.b);
this.c = other.c == 0 ? this.c : other.c;
}
}
A a1 = new A();
A a2 = new A();
a1.a ="a prop";
a2.c = 34;
a1.merge(a2);
A.merge可能会返回一个新的A对象,而不是修改当前对象。
那就是我要建议的-无论如何,您都必须在任何地方清楚地编写规则-除非绝对不得已,否则您不想在代码中添加" Magic"之类的反射,并且这种情况绝不那么困难。
NullPointerException,因为this.b可能是null,并且没有满足b的"来自obj2的相同数组列表"的请求
我正在使用Spring Framework。我在一个项目上面临着同样的问题。
为了解决这个问题,我使用了BeanUtils类和上述方法,
public static void copyProperties(Object source, Object target)
这是一个例子
public class Model1 {
private String propertyA;
private String propertyB;
public Model1() {
this.propertyA ="";
this.propertyB ="";
}
public String getPropertyA() {
return this.propertyA;
}
public void setPropertyA(String propertyA) {
this.propertyA = propertyA;
}
public String getPropertyB() {
return this.propertyB;
}
public void setPropertyB(String propertyB) {
this.propertyB = propertyB;
}
}
public class Model2 {
private String propertyA;
public Model2() {
this.propertyA ="";
}
public String getPropertyA() {
return this.propertyA;
}
public void setPropertyA(String propertyA) {
this.propertyA = propertyA;
}
}
public class JustATest {
public void makeATest() {
// Initalize one model per class.
Model1 model1 = new Model1();
model1.setPropertyA("1a");
model1.setPropertyB("1b");
Model2 model2 = new Model2();
model2.setPropertyA("2a");
// Merge properties using BeanUtils class.
BeanUtils.copyProperties(model2, model1);
// The output.
System.out.println("Model1.propertyA:" + model1.getPropertyA(); //=> 2a
System.out.println("Model1.propertyB:" + model1.getPropertyB(); //=> 1b
}
}
只是容纳布尔同步。区分大小写(驼色表示法)
public boolean merge(Object obj){
if(this.equals(obj)){
return false;
}
if(!obj.getClass().isAssignableFrom(this.getClass())){
return false;
}
Method[] methods = obj.getClass().getMethods();
for(Method fromMethod: methods){
if(fromMethod.getDeclaringClass().equals(obj.getClass())
&& (fromMethod.getName().matches("^get[A-Z].*$")||fromMethod.getName().matches("^is[A-Z].*$"))){
String fromName = fromMethod.getName();
String toName ;
if(fromName.matches("^get[A-Z].*")){
toName = fromName.replace("get","set");
}else{
toName = fromName.replace("is","set");
}
try {
Method toMetod = obj.getClass().getMethod(toName, fromMethod.getReturnType());
Object value = fromMethod.invoke(this, (Object[])null);
if(value != null){
toMetod.invoke(obj, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return true;
}
如果为属性创建获取器和设置器,则可以使用Commons BeanUtils中的copyProperties方法。
在非常特殊的情况下,您似乎想要一个新的对象,该对象从两个实例中获取真实值。这是实现该目的的实现。该方法应该添加到类A中,以便它可以访问字段。
public A specialMergeWith(A other) {
A result = new A();
result.a = (a == null ? other.a : a);
result.b = (b == null ? other.b : b);
result.c = (c == DEFAULT_VALUE ? other.c : c);
return result;
}
将此方法添加到您的POJO,然后像myObject.merge(newObject)一样使用它。它使用泛型来遍历POJO的字段,因此您不会提及任何字段名:
/**
* Fill current object fields with new object values, ignoring new NULLs. Old values are overwritten.
*
* @param newObject Same type object with new values.
*/
public void merge(Object newObject) {
assert this.getClass().getName().equals(newObject.getClass().getName());
for (Field field : this.getClass().getDeclaredFields()) {
for (Field newField : newObject.getClass().getDeclaredFields()) {
if (field.getName().equals(newField.getName())) {
try {
field.set(
this,
newField.get(newObject) == null
? field.get(this)
: newField.get(newObject));
} catch (IllegalAccessException ignore) {
// Field update exception on final modifier and other cases.
}
}
}
}
}
如果您不想在添加/删除字段等时不断维护该方法,这是最好的解决方案。Id可以走得更远,不要忽略" IllegalAccessException",因为它可能会隐藏问题,但是对于某些特定字段,您有不想使用的替代情况合并-或者您想以其他方式处理等
有一个动态解决方案可以合并需要反射和递归的任何两个对象。
public < T > T merge(T local, T remote, ArrayList listOfClass)
throws IllegalAccessException, InstantiationException {
Class< ? > clazz = local.getClass();
Object merged = clazz.newInstance();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object localValue = field.get(local);
Object remoteValue = field.get(remote);
if (localValue != null) {
if (listOfClass.contains(localValue.getClass().getSimpleName())) {
field.set(merged, this.merge(localValue, remoteValue, listOfClass));
} else {
field.set(merged, (remoteValue != null) ? remoteValue : localValue);
}
} else if (remoteValue != null) {
field.set(merged, remoteValue);
}
}
return (T) merged;
}
变量说明:
local:另一个对象将合并到的对象
remote:将合并到本地对象的对象
listOfClass:给定对象中的自定义类的ArrayList
该函数返回一个合并的对象,很好。
荣誉! :)