比较两个对象(排除某些字段)- Java

nc1teljy  于 2023-02-15  发布在  Java
关注(0)|答案(6)|浏览(226)

我需要比较同一个类的两个对象,但不包括某些字段。

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

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

怎样才能知道除了createdAt和updatedAt值之外,上述类中的两个对象是否相等?由于该类中有很多字段,我不想逐一比较它们。
请不要给出AssertJ的递归比较解决方案,因为我在UnitTests中不需要它。
提前感谢您!

zc0qhyus

zc0qhyus1#

使用Apache公共语言

CompareToBuilder.reflectionCompare(expected, argument, "someField");
bweufnob

bweufnob2#

如果不能覆盖Object::equalsObject::hashCode,我们可以使用Comparator API来构造相应的比较器:

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

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

cmssoen2

cmssoen23#

无需编写任何代码的最快方法是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
}

Lombok可以做的远不止这些,关于@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; 
}
uqxowvwt

uqxowvwt4#

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

n9vozmp45#

除了比较器和hashCode()/equals方法,您还可以使用反射。
1.创建注解以排除某些字段:

  • 黑名单 * 示例:
@Retention(RetentionPolicy.RUNTIME) //
@Target(ElementType.FIELD) //on class level
public @interface IngoreForEqualCheck { /* tagging only */ }

1.通过对对象的类使用pClass.getFields()和/或pClass.getDeclaredFields(),使用反射分析要比较的对象。这甚至可以是不同的类。
1.迭代所有未标记为忽略的字段,比较值。

优化

1.要扩展上述黑名单,还需要引入 * 白名单 *:还创建注解UseForEqualCheck以仅检查这些字段。
1.为了提高速度,在分析各个类及其字段时,您可以创建要检查的字段的可迭代列表,而不是每次都进行反射字段分析,只需使用列表即可。
1.通常你会在检测到的字段值上使用equals(),你也可以a)用另一个自定义注解标记类,或者B)检查字段是否有白名单/黑名单注解,这样你就可以可靠地将新方法用于嵌入的/继承的/委托的注解类。

警告

和所有的反射一样,在分析类的层次结构时,你可能会遇到麻烦,因为类在编译过程(javac)中被注解预处理器或字节码编织修改了。这主要指Java EE aka Jakarta,但也可能发生在任何地方,只要你的代码中包含了后台功能,或者运行时行为发生了变化,比如注入,面向方面的库等等。

efzxgjgh

efzxgjgh6#

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

相关问题