Объединение двух объектов в Java
У меня есть два объекта одного типа.
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}
Ответы
Ответ 1
Может быть что-то вроде
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
вместо изменения текущего.
Ответ 2
Это работает до тех пор, пока у вас есть POJO со своими собственными геттерами и сеттерами. Этот метод обновляет obj с ненулевыми значениями от update. Он вызывает setParameter() в obj с возвращаемым значением 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();
}
}
}
}
Ответ 3
Простое размещение булевой синхронизации. и чувствительный к регистру (обозначение верблюда)
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;
}
Ответ 4
Я использую 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
}
}
Ответ 5
Если вы создаете геттеры и сеттеры для атрибутов, вы можете использовать метод copyProperties из Commons BeanUtils.
Ответ 6
В вашем очень конкретном случае похоже, что вы хотите новый объект, который принимает реальные значения из обоих экземпляров. Вот реализация, которая сделает это. Метод должен быть добавлен в класс 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;
}
Ответ 7
Добавьте этот метод к вашему 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.
}
}
}
}
}
Ответ 8
Существует динамическое решение для объединения любых двух объектов, которые требуют отражения и рекурсии.
public <T> T merge(T local, T remote, ArrayList<String> 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 пользовательских классов в данном объекте
Функция возвращает объединенный объект, который можно использовать.
Престижность! :)