0%

Lombok 对象判等问题

Lombok 的 @Data 注解会帮我们实现 equals 和 hashcode 方法,但是有继承关系时, Lombok 自动生成的方法可能就不是我们期望的了。

我们先来研究一下其实现:定义一个 Person 类型,包含姓名和身份证两个字段:

1
2
3
4
5
6
7
8
9
10
11
12
@Data
class Person {
private String name;
private String identity;

public Person() {}

public Person(String name, String identity) {
this.name = name;
this.identity = identity;
}
}

对于身份证相同、姓名不同的两个 Person 对象:

1
2
3
Person person1 = new Person("zhuye","001");
Person person2 = new Person("Joseph","001");
log.info("person1.equals(person2) ? {}", person1.equals(person2)); // false

使用 equals 判等会得到 false。如果你希望只要身份证一致就认为是同一个人的话,可以使用 @EqualsAndHashCode.Exclude 注解来修饰 name 字段,从 equals 和 hashCode 的实现中排除 name 字段:

1
2
@EqualsAndHashCode.Exclude
private String name;

修改后得到 true。打开编译后的代码可以看到,Lombok 为 Person 生成的 equals 方法的实现,确实只包含了 identity 属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof LombokEquealsController.Person)) {
return false;
} else {
LombokEquealsController.Person other = (LombokEquealsController.Person)
if (!other.canEqual(this)) {
return false;
} else {
// 只有 identity 字段比较
Object this$identity = this.getIdentity();
Object other$identity = other.getIdentity();
if (this$identity == null) {
if (other$identity != null) {
return false;
}
} else if (!this$identity.equals(other$identity)) {
return false;
}
return true;
}
}
}

但到这里还没完,如果类型之间有继承,Lombok 会怎么处理子类的 equals 和 hashCode 呢?我们来测试一下,写一个 Employee 类继承 Person,并新定义一个公司属性:

1
2
3
4
5
6
7
8
@Data
public class Employee extends Person {
private String company;
public Employee(String name, String identity, String company) {
super(name, identity);
this.company = company;
}
}

在如下的测试代码中,声明两个 Employee 实例,它们具有相同的公司名称,但姓名和身份证均不同:

1
2
3
Employee employee1 = new Employee("zhuye","001", "bkjk.com");
Employee employee2 = new Employee("Joseph","002", "bkjk.com");
log.info("employee1.equals(employee2) ? {}", employee1.equals(employee2));

很遗憾,结果是 true,显然是没有考虑父类的属性,而认为这两个员工是同一人,说明 @EqualsAndHashCode 默认实现没有使用父类属性。

为解决这个问题,我们可以手动设置 callSuper 开关为 true,来覆盖这种默认行为:

1
2
3
4
5
@Data
@EqualsAndHashCode(callSuper = true)
public class Employee extends Person {
// ...
}

修改后的代码,实现了同时以子类的属性 company 加上父类中的属性 identity,作为 equals 和 hashCode 方法的实现条件(实现上其实是调用了父类的 equals 和 hashCode)。

------ 本文结束------