2017-08-29 113 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類,則可能需要覆蓋更多方法。