簡體   English   中英

比較排除某些字段的兩個對象 - Java

[英]Compare two objects excluding some fields - Java

我需要比較相同 class 的兩個對象,不包括某些字段。

public final class Class1 {
  private String a;
  private String b;
  private String c;
:
:
:

  private String z;
  private Date createdAt; 
  private Date updatedAt; 
} 

我如何才能找到上述 class 的兩個對象是否相等,不包括 createdAt 和 updatedAt 值? 由於這個class中有很多字段,我不想一一比較。

請不要提供 AssertJ 的遞歸比較解決方案,因為 UnitTests 不需要它。

先感謝您!

如果覆蓋Object::equalsObject::hashCode不是一個選項,我們可以使用Comparator器 API來構造一個相應的比較器

final Comparator<Class1> comp = Comparator.comparing(Class1::getA)
        .thenComparing(Class1::getB)
        .thenComparing(Class1::getC)
        .
        .
        .
        .thenComparing(Class1::getZ);

不幸的是,如果不比較所有應該相等的字段,就沒有辦法做到這一點。

嘗試重寫 equals 方法,如下所示:

import java.util.Date;
import java.util.Objects;

public final class Class1 {
    private String a;
    private String b;
    private String c;
    private String z;
    private Date createdAt;
    private Date updatedAt;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Class1 class1 = (Class1) o;
        return Objects.equals(a, class1.a) && Objects.equals(b, class1.b) && Objects.equals(c, class1.c) && Objects.equals(z, class1.z);
    }

    @Override
    public int hashCode() {
        return Objects.hash(a, b, c, z);
    }
}

除了 Comparator 和hashCode() / equals方法之外,您還可以使用反射。

  1. 創建注釋以排除某些字段:

黑名單示例:

@Retention(RetentionPolicy.RUNTIME) //
@Target(ElementType.FIELD) //on class level
public @interface IngoreForEqualCheck { /* tagging only */ }
  1. 通過在對象的 class 上使用pClass.getFields()和/或pClass.getDeclaredFields() ,使用反射來分析要比較的對象。 這甚至可能是不同的類。

  2. 遍歷所有未標記為被忽略的字段,比較值。

優化

  1. 要從上面擴展黑名單,還需要引入白名單:還創建一個注釋UseForEqualCheck以僅檢查這些字段。

  2. 為了提高速度,在分析各個 class 及其字段時,您可以創建要檢查的字段的可迭代列表,而不是每次都進行反射字段分析,只需使用列表即可。

  3. 通常您會在檢測到的字段值上使用equals() 您還可以 a) 使用另一個自定義注釋標記 class,或 b) 檢查字段中的任何白名單/黑名單注釋,以便您可以可靠地將新方法用於嵌入/繼承/委托的注釋類。

警告

與所有反射一樣,在分析類的層次結構時可能會遇到麻煩,這些類在編譯過程 (javac) 期間已通過注釋預處理器或字節碼編織進行了修改。 這主要是指 Java EE aka Jakarta,但可能發生在代碼中包含幕后功能或更改運行時行為的任何地方,例如注入、面向方面的庫等。

不寫任何代碼最快的方法是Lombok

Lombok 是 java 中最常用的庫之一,它可以從您的項目中刪除大量樣板代碼。 如果您需要詳細了解它的功能和功能,請點擊此處go。

實現所需內容的方法非常簡單:

// Generate the equals and HashCode functions and Include only the fields that I annotate with Include
@EqualsAndHashCode(onlyExplicitlyIncluded = true) 
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public class Class1
{

  @EqualsAndHashCode.Include // Include this field
  private Long identity;
  
  private String testStr1; // This field is not annotated with Include so it will not be included in the functions.

  // ... any other fields
}

龍目島可以做的遠不止這些。 有關@EqualsAndHashCode的更多信息,請參閱

您始終可以使用@EqualsAndHashCode.Exclude更快地解決您的用例:

@EqualsAndHashCode
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public final class Class1 {
  private String a;
  private String b;
  private String c;
:
:
:

  private String z;

  @EqualsAndHashCode.Exclude
  private Date createdAt; 
  @EqualsAndHashCode.Exclude
  private Date updatedAt; 
} 

@Renis1235 的 Lombok 解決方案是最簡單的。 但是,如果由於某種原因在不同的上下文中,equals 可能意味着不同的東西,並且您不想更改默認的 Equals 和 Hashcode 行為,我建議您為要排除的字段分配默認值,然后使用 equals。 (假設您可以更改對象)

例如:

Class1 a = ...;
Class1 b = ...;
a.createdAt = null;
b.createdAt = null;
a.updatedAt = null;
b.updatedAt = null;
boolean isEqualExcludingTimestamps = a.equals(b);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM