Java如何比較兩個對象並獲取不相等的字段詳解
寫在前面
在工作中,我們經常會遇到這樣的需求——比較兩個對象是否相等,如果不相等的話,取出不相等的字段。
以下這些場景都需要我們對一個對象進行比較:
- 數據比對
- 做單元測試斷言對象是否相等
- 前端要求對不相等的字段進行高亮顯示
這種需求其實是非常簡單的,但是如何優雅地解決這一類需求呢?
通常的做法是重寫對象的 equals 方法。但是重寫 equals 方法有很多缺點,例如:
- 每次對象屬性有變更,一定要記得再重寫(放心,你一定會忘記的)
- 每個對象隻能有一個 equals 方法,但是可能你會需要不同的比對規則
- 隻能對比兩個對象是否相等,無法具體知道哪個屬性不等
- 自動生成的 equals 方法無法基於 getter 方法進行比對
- 對象來自第三方依賴,無法重寫 equals 方法
因此,實現一個通用的比對器可以減少很多不必要的麻煩,幫助我們很好地完成這一類的需求。
緣起
我是在做數據同步的時候有這個需求,我要將數據庫的數據通過一定的規則導入到 ES 中,導入完成之後,如何比對兩邊的數據是否一致呢?這時候一個好用的比對器就是我非常好的幫手。
另外,我在做單元測試的時候發現,經常會需要將被測方法的返回值和期望的結果做 assertEquals 斷言這時這個比對器也非常有幫助。我發現很多同事經常會遇到類似的需求。
於是,我找時間自己實現瞭一下。
實現
使用反射對傳入的對象進行比對,提供瞭基於字段的比較器和基於 Getter 方法的對比器,並且充分考慮擴展性,使用者可以重寫字段的比對規則。功能相對簡單,代碼實現也不難,而且做瞭很多註釋,具體實現可以直接查看源碼。
項目地址:https://github.com/dadiyang/equator
UML圖:
使用方法
因為已經上傳到瞭 maven 倉庫中,我們使用非常方便:
添加 maven 依賴
<dependency> <groupId>com.github.dadiyang</groupId> <artifactId>equator</artifactId> <version>1.0.3</version> </dependency>
初始化並調用方法
Equator equator = new GetterBaseEquator(); User user1 = new User(...); User user2 = new User(...); // 判斷屬性是否完全相等 equator.isEquals(user1, user2); // 獲取不同的屬性 List<FieldInfo> diff = equator.getDiffFields(user1, user2);
擴展
我們可以通過繼承並重寫 isFieldEquals 方法自定義比對規則,例如我們在做單元測試的時候,對於 Date 類型的字段的比對,通常數據庫不保存毫秒數,而我們 new 出來的 Date 對象則包含瞭毫秒數,因此我們在對包含 Date 類型字段的對象做比對的時候需要忽略日期的毫秒數。這時就可以通過重寫isFieldEquals 方法來自定義瞭:
/** * 日期在數據庫不保存毫秒數,因此需要特殊處理,比對時間時,忽略毫秒數 * * @author dadiyang * @date 2019/3/23 */ public class MmInsensitiveEquator extends GetterBaseEquator { @Override protected boolean isFieldEquals(FieldInfo fieldInfo) { if (fieldInfo.getFirstVal() instanceof Date) { Date first = (Date) fieldInfo.getFirstVal(); Date second = (Date) fieldInfo.getSecondVal(); if (Objects.equals(first, second)) { return true; } // 忽略毫秒數 return Objects.equals(Math.round(first.getTime() / 1000), Math.round(second.getTime() / 1000)); } return super.isFieldEquals(fieldInfo); } }
後記
對象比對是一個非常小的需求,通常我們隻會寫一個工具類來完成。但是寫一個工具類在各個項目間隨處拷貝,非常不優雅,給整個團隊帶來很多不必要的維護成本。而且擴展性比較差,有任何差異就需要寫很多代碼去實現。
這時,如果我們從具體解決某一個需求的視角上升到解決一類需求,那麼就能想出更加通用和優雅的解決方案瞭。一個個具體的需求是無窮無盡的,以有限的人生去解決無限的需求,殆矣;但是將它們歸類之後,我們會發現,需求的種類是有限的。
附:JAVA判斷(獲取)兩個相同對象不同的數據
項目中需要獲取修改前和修改後的不同數據並進行保存。
不知道高大上的做法,就寫個工具類。
package com.shiyan.utils.object; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; public class GetDifference { public static Map<String, String> getUser(Object a, Object b) throws IllegalArgumentException, IllegalAccessException { Map<String, String> map = new HashMap<String, String>(); Field[] declaredFields2 = a.getClass().getDeclaredFields(); Field[] declaredFields23 = b.getClass().getDeclaredFields(); StringBuilder xiugaiqian = new StringBuilder(); StringBuffer xiugaihou = new StringBuffer(); for (int i = 0; i < declaredFields2.length; i++) { declaredFields2[i].setAccessible(true); declaredFields23[i].setAccessible(true); if (declaredFields2[i].get(a) != null && declaredFields23[i].get(b) != null) { if (!declaredFields2[i].get(a).equals(declaredFields23[i].get(b))) { xiugaiqian.append(declaredFields2[i].getName() + ":" + declaredFields2[i].get(a)).append(","); xiugaihou.append(declaredFields23[i].getName() + ":" + declaredFields23[i].get(b)).append(","); } } else if (declaredFields2[i].get(a) == null && declaredFields23[i].get(b) != null) { xiugaiqian.append(declaredFields2[i].getName() + ":" + null).append(","); xiugaihou.append(declaredFields23[i].getName() + ":" + declaredFields23[i].get(b)).append(","); } else if (declaredFields2[i].get(a) != null && declaredFields23[i].get(b) == null) { xiugaiqian.append(declaredFields2[i].getName() + ":" + declaredFields2[i].get(a)).append(","); xiugaihou.append(declaredFields23[i].getName() + ":" + null).append(","); } } if (StringUtils.isNoneBlank(xiugaiqian.toString()) && StringUtils.isNoneBlank(xiugaihou.toString())) { map.put(xiugaiqian.toString().substring(0, xiugaiqian.length() - 1), xiugaihou.toString().substring(0, xiugaihou.length() - 1)); } return map; } }
測試代碼
/** * * Description: * @Author:xieyuxin * @param * @param 設定文件 * @throws * @return String * @Exception 異常對象 */ package com.shiyan.test; import java.util.Map; import com.shiyan.utils.object.GetDifference; public class ObjectGetEerro { public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException { Persion p1 = new Persion.Builder().age(12).name("張三").t("2018-10-20").build(); Persion p2 = new Persion.Builder().age(16).name("1111").build(); Map<String, String> user = GetDifference.getUser(p1, p2); for(Map.Entry<String, String> entry : user.entrySet()){ System.out.println("修改前 :" + entry.getKey()); System.out.println("修改後 :" + entry.getValue()); } } }
到此這篇關於Java如何比較兩個對象並獲取不相等字段的文章就介紹到這瞭,更多相關Java比較兩個對象內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java開發反射機制的實戰經驗總結
- Java循環對bean的屬性進行賦值的實現
- Java基礎之淺談hashCode()和equals()
- java中的空指針異常情況以及解決方案
- Java如何重寫object類的equals方法詳解