繁体   English   中英

如何“合并”同一个类的两个对象

[英]How to “merge” two objects of the same class

代码是常规的,但答案可以是Groovy或Java。 我有一个包含此字段的Person类:

class Person(){
String name
String lasName
}

我有一个方法,从同一个类返回两个对象。 一个对象包含一些字段,另一个包含其余字段,在我的示例中它将是这样的:

person1 = "name : Jon"
person2 = "lastName : Snow"

我需要的是将person1所有空字段替换为person2字段,如果这不是null,在我们的示例中,输出将是:

person1.merge(person2)
person1= "name : Jon, lastName : Snow"

在没有编写所有字段(使用某种循环)的情况下,Java或Groovy上是否有任何方法可以执行与此类似的操作?

如果没有任何默认方法可以使用,我如何遍历类中的所有字段?

刚刚使用反射测试。 期望的输出是

merged person:Person{name=John, lastName=Snow}     



public static void testReflection() {
        Person p1 = new Person("John", null);
        Person p2 = new Person(null, "Snow");
        Person merged = (Person) mergePersons(p1, p2);
        System.out.println("merged person:" + merged);
}

public static Object mergePersons(Object obj1, Object obj2) throws Exception {
    Field[] allFields = obj1.getClass().getDeclaredFields();
    for (Field field : allFields) {
        if (Modifier.isPublic(field.getModifiers()) && field.isAccessible() && field.get(obj1) == null && field.get(obj2) != null) {
            field.set(obj1, field.get(obj2));
        }
    }
    return obj1;
}

mergePersons接受两个对象。

然后它遍历所有字段并验证第一个对象是否具有空值。 如果是,则验证第二个对象是否未被清零。

如果这是真的,则将值分配给第一个Object。

提供此解决方案您只能访问公共数据。 如果您还想访问私人数据,则需要删除修改器验证并设置是否可访问,如下所示:

public static Object mergePersons(Object obj1, Object obj2) throws Exception {
    Field[] allFields = obj1.getClass().getDeclaredFields();
    for (Field field : allFields) {

        if (!field.isAccessible() && Modifier.isPrivate(field.getModifiers())) 
            field.setAccessible(true);
        if (field.get(obj1) == null && field.get(obj2) != null) {
            field.set(obj1, field.get(obj2));
        }
    }
    return obj1;
}

鉴于Groovy字段是作为具有支持字段的getter / setter对实现的,您可以在Groovy中以这种方式执行:

static <T> void merge(T from, T to) {
    from.metaClass.properties.findAll { p ->
        p.getProperty(to) == null &&
            p.getProperty(from) != null &&
            to.respondsTo(MetaProperty.getSetterName(p.name))
    }
    .each {
        p -> p.setProperty(to, p.getProperty(from))
    }
}

你将不得不走反射路线。 我假设你有一个默认的构造函数,否则以下将无法正常工作。 此外,它需要两种相同的类型。

public static <T> T mergeObjects(T first, T second) throws IllegalAccessException, InstantiationException {
        Class<?> clazz = first.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Object returnValue = clazz.newInstance();
        for (Field field : fields) {
            field.setAccessible(true);
            Object value1 = field.get(first);
            Object value2 = field.get(second);
            Object value = (value1 != null) ? value1 : value2;
            field.set(returnValue, value);
        }
        return (T) returnValue;
    }

这是一个例子

import java.lang.reflect.Field;
public class Merge2Obj {

    private String name;
    private String lasName;

    public Merge2Obj() {
        super();
    }

    public Merge2Obj(String name, String lasName) {
        super();
        this.name = name;
        this.lasName = lasName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLasName() {
        return lasName;
    }

    public void setLasName(String lasName) {
        this.lasName = lasName;
    }

    public static <T> T mergeObjects(T first, T second) throws IllegalAccessException, InstantiationException {
        Class<?> clazz = first.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Object returnValue = clazz.newInstance();
        for (Field field : fields) {
            field.setAccessible(true);
            Object value1 = field.get(first);
            Object value2 = field.get(second);
            Object value = (value1 != null) ? value1 : value2;
            field.set(returnValue, value);
        }
        return (T) returnValue;
    }

     public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Merge2Obj obj1 = new Merge2Obj("ABC", null);
        Merge2Obj obj2 = new Merge2Obj("PQR", "LMN");

        Merge2Obj obj3 = mergeObjects(obj1, obj2);

        System.out.println(obj3.name);
        System.out.println(obj3.lasName);
    }

}

这是一种快速(和冒昧)的方法,与使用字段上的反射基本相同,但使用:

  1. Groovy在java.lang.Object上的内置getProperties()方法 ,它为我们提供了一个属性名称和值的Map
  2. Groovy的默认Map构造函数 ,它允许在给定属性Map的情况下创建Object的实例。

鉴于这两个功能,您可以将要合并的每个对象描述为其属性的Map,去掉null值的条目,将Maps组合在一起(并删除只读的烦人的'class'条目),并使用合并Map以构建合并的实例。

class Person {
    String first, last, middle
}

def p1 = new Person(first: 'bob')
def p2 = new Person(last: 'barker')

Person merged = (p1.properties.findAll { k, v -> v }  // p1's non-null properties
               + p2.properties.findAll { k, v -> v }) // plus p2's non-null properties
               .findAll { k, v -> k != 'class' }      // excluding the 'class' property

assert merged.first == 'bob'
assert merged.last == 'barker'
assert merged.middle == null

假设有一个带有getter和setter的可变数据类,Apache BeanUtils可能适合您的需求。

默认情况下, BeanUtilBeansBean.copyProperties(Object dest, Object orig)查找T orig.get*()dest.set*(T value) ,并使用前者的结果调用后者。

但是您可以注入一个自定义的PropertyUtilsBean ,因此您可以包装默认的一个以防止它替换非null属性:

public NoClobberPropertyUtilsBean extends PropertyUtilsBean {
     @Override
     public void setSimpleProperty((Object bean,
                          String name,
                          Object value)
                   throws IllegalAccessException,
                          InvocationTargetException,
                          NoSuchMethodException {
          if(getProperty(bean,name) == null) {
              super.setSimpleProperty(bean,name,value);
          }
     }
}

现在您可以合并:

BeanUtilsBean beanUtils = new BeanUtilsBean(new ConvertUtilsBean(), new NoClobberPropertyUtilsBean());
Person merged = new Person();
beanUtils.copyProperties(person1);
beanUtils.copyProperties(person2);

如果两个源中的属性都为非null,则第一个copyProperties获胜。

你当然可以改变语义,例如,如果守卫是if(value != null) ,它会表现出不同的方式。

在一个层面上,BeanUtils只是其他答案所暗示的反射操作的包装器。 是否需要额外的抽象级别取决于您。 如果要支持map / list成员或BeanUtils的DynaBean类,则可能需要覆盖更多方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM