比较排除某些字段的两个对象 - 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; 
} 

除了 createdAt 和 updatedAt 值之外,我如何才能确定上述 class 的两个对象是否相等?由于这个class的字段比较多,不想一一比较了

请不要给出 AssertJ 的递归比较解决方案,因为我不需要它用于单元测试。

提前致谢!

尝试重写 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);
    }
}

如果覆盖Object::equals and Object::hashCode is not an option, we can use the Comparator API构造相应的比较器:

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

不幸的是,如果不比较所有应该相等的字段,就无法做到这一点。

除了Comparator和hashCode()/equals方法,你还可以使用Reflections。

  1. 创建注释以排除某些字段:

黑名单示例:

@Retention(RetentionPolicy.RUNTIME) //
@Target(ElementType.FIELD) //on class level
public @interface IngoreForEqualCheck { /* tagging only */ }
  1. 使用反射来分析要比较的对象,方法是在对象的 class 上使用 pClass.getFields() and/or pClass.getDeclaredFields()。这甚至可能不同 classes.

  2. 遍历所有未标记为忽略的字段,比较值。

优化

  1. 为了扩展上面的黑名单,还要引入白名单:还要创建一个注解UseForEqualCheck来只检查那些字段。

  2. 为了提高速度,在分析各自的class及其字段时,您可以创建要检查的字段的可迭代列表,而不是每次都进行反射字段分析,只需使用列表。

  3. 通常您会在检测到的字段值上使用 equals()。您还可以 a) 用另一个自定义注释标记 class,或者 b) 检查字段中是否有任何 whitelisting/blacklisting 注释,这样您就可以可靠地将新方法用于 embedded/inherited/delegated 注释 classes.

警告

与所有反射一样,在分析 classes 的层次结构时,您可能会遇到麻烦,这些层次结构在编译过程 (javac) 期间已被注释预处理器或字节码编织修改。这主要是指 Java EE aka Jakarta,但也可能发生在代码中包含幕后功能或运行时行为发生变化的任何地方,例如注入、面向方面的库等。

无需编写任何代码的最快方法是Lombok

Lombok 是 java 中最常用的库之一,它从您的项目中删除了大量样板代码。如果您需要阅读更多有关它的功能和作用的信息,请转到 here.

实现所需内容的方法非常简单:

// 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 的更多信息,请参阅 this

您始终可以使用 @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; 
}