注解介绍
Lombok 注解可以让我们免去每次编写 Java 类的时候需要添加 getter() 和 sertter() 等通用方法。@Data 注解相当于同时添加 @Getter、@Setter、@EqualsAndHashCode 等注解。但是使用 @Data 需要注意该注解生成的 equals 方法只是针对当前类中的字段值进行比较,并不会比较父类中的字段。因此如果使用该注解的类有父类就需要注意添加一条注解 @EqualsAndHashCode(callSuper = true)
。
问题复现
- 创建父类
import lombok.Data;
@Data
public class ParentClass {
private String parentField;
}
- 创建子类
import lombok.Data;
@Data
public class ChildClass extends ParentClass{
private String childField;
}
- 测试比较
public class LombokTest {
public static void main(String[] args) {
ChildClass child1 = new ChildClass();
ChildClass child2 = new ChildClass();
child1.setChildField("same");
child2.setChildField("same");
child1.setParentField("a");
child2.setParentField("b");
System.out.println("child1 equals child2: " + child1.equals(child2));
System.out.println("child1: " + child1);
System.out.println("child2: " + child2);
}
}
- 输出结果
child1 equals child2: true
child1: ChildClass(childField=same)
child2: ChildClass(childField=same)
显然 child1 和 child2 中从父类继承的字段 parentField 不同,但是输出结果却显示两个类相同。
我们在子类中添加两条注解,修改后子类如下:
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ChildClass extends ParentClass {
private String childField;
}
运行上面的测试程序,输出如下:
child1 equals child2: false
child1: ChildClass(super=ParentClass(parentField=a), childField=same)
child2: ChildClass(super=ParentClass(parentField=b), childField=same)
此时判断 child1 和 child2 是否相等就会使用父类中的信息,可以看出输出结果为不相等。上面 ChildClass 类上面我添加了另外一个注解@ToString(callSuper = true)
,比较两次输出就可以看出使用该注解可以使子类中的 toString() 方法输出父类中的字段信息。
原理解析
比较子类添加@EqualsAndHashCode(callSuper = true)
注解前后的字节码反编译结果,注释处可以发现两个注解均引入对父类的引用。
- 添加注解前 equals 方法
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof ChildClass)) {
return false;
} else {
ChildClass other = (ChildClass)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$childField = this.getChildField();
Object other$childField = other.getChildField();
if (this$childField == null) {
if (other$childField != null) {
return false;
}
} else if (!this$childField.equals(other$childField)) {
return false;
}
return true;
}
}
}
- 添加注解后 equals 方法
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof ChildClass)) {
return false;
} else {
ChildClass other = (ChildClass)o;
if (!other.canEqual(this)) {
return false;
} else if (!super.equals(o)) { // 此处增加了对父类equals方法的调用
return false;
} else {
Object this$childField = this.getChildField();
Object other$childField = other.getChildField();
if (this$childField == null) {
if (other$childField != null) {
return false;
}
} else if (!this$childField.equals(other$childField)) {
return false;
}
return true;
}
}
}
比较添加@ToString(callSuper = true)
注解前后的反编译代码也有同样结论:
- 添加注解前 toString 方法
public String toString() {
return "ChildClass(childField=" + this.getChildField() + ")";
}
- 添加注解后 toString 方法
public String toString() {
return "ChildClass(super=" + super.toString() + ", childField=" + this.getChildField() + ")";
// 调用父类toString方法
}