2017-08-29 117 views
1

代码很流行,但答案可以是Groovy或Java。 我有这个领域的一个Person类:如何“合并”同一类别的两个对象

class Person(){ 
String name 
String lasName 
} 

我有一个返回同一类的两个对象的方法。一些领域和其他与剩下的一个对象,在我的例子这将是这样的:

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

我需要的是加入到person1所有空字段不在person2空,在我们的例子中,输出将是:

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

是否有关于Java或Groovy任何方法来做类似这样的不写我的所有领域的东西(用某种循环)?

如果没有使用任何默认方法,我怎样才能遍历一个类中的所有字段?

+0

您是否尝试为您的数据使用反射? –

+0

SDK不提供这样的方法。你可以使用反射API来做到这一点。但真正的问题是 - 为什么你需要这样做?应该总是有一个更好的方法,不会导致这种情况。 – Prashant

+0

我被告知要避免使用反射,因为它会影响性能,您怎么看待这个问题?编辑:是的,@Prashant有更好的方法,但我不能改变传入的数据(我收到三个不同的对象,我需要在同一类中合并,我不想更改我的代码,如果某些字段添加未来...) – Blazerg

回答

0

这是一种快速(和放肆)的方法,基本上与在田间使用反射相同,但是使用:

  1. Groovy的内置getProperties() method上java.lang.Object中,这为我们提供了地图的属性名称和值
  2. Groovy的default Map constructor,它允许使用创建给予了地图属性的对象的实例。

鉴于这两项功能,您可以描述每个对象要合并为一个地图及其属性,剥离出null -valued条目相结合的地图(和去除讨厌的“类”条目是只读),并使用合并的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 
1

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

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接受两个对象。

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

如果这是真的,它将值赋给第一个对象。

提供此解决方案,您只能访问公共数据。如果您要访问私人数据藏汉,你需要像以前一样删除修改验证和访问是否设置:

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

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

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的领域实现为一个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)) 
    } 
} 
0

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

默认BeanUtilBeansBean.copyProperties(Object dest, Object orig)寻找T orig.get*()dest.set*(T value)对,并调用后者与前者的结果。

但你可以注入定制PropertyUtilsBean,让你可以换一个默认,以防止它替换非空的属性:

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

如果:

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

现在你可以用合并两个来源中的属性都非空,第一个copyProperties获胜。

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

在一个层次上,BeanUtils仅仅是其他答案提出的反射操作的一种包装。您是否想要额外的抽象级别取决于您。如果您想支持地图/列表成员,或者BeanUtils的DynaBean类,则可能需要覆盖更多方法。