在 java 中使用 Lombok的注解@Builder时,对象属性有默认值时会碰到默认值不会生效的坑。
Lombok 测试版本 1.18.28:
示例代码:
package com;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@EqualsAndHashCode
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Demo {
private String name;
private boolean bTest1 = true;
private Boolean bTest2 = true;
public static void main(String[] args) {
Demo demo = new DemoBuilder().build();
System.out.println("lombok Builder 生成的对象: "+demo);
System.out.println("默认构造函数生成的对象: "+new Demo());
}
}
代码中使用Lombok 的@Builder 注解生成的DemoBuilder,生成一个对象:
看一下对象中的属性默认值,使用DemoBuilder(第一行)与java默认构造函数(第二行)new对象后有什么不同结果:
使用DemoBuilder构造的java对象属性初始化完全和java的默认构造函数初始化的不一样,非常坑。
要想弄明白怎么回事,只能去看Lombok的@Builder是如何编译生成java代码(可跳过直接看重点截图):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com;
import java.io.PrintStream;
public class Demo {
private String name;
private boolean bTest1 = true;
private Boolean bTest2 = true;
public static void main(String[] args) {
Demo demo = (new DemoBuilder()).build();
System.out.println("lombok Builder 生成的对象: " + demo);
PrintStream var10000 = System.out;
Demo var10001 = new Demo();
var10000.println("默认构造函数生成的对象: " + var10001);
}
public static DemoBuilder builder() {
return new DemoBuilder();
}
public String getName() {
return this.name;
}
public boolean isBTest1() {
return this.bTest1;
}
public Boolean getBTest2() {
return this.bTest2;
}
public void setName(String name) {
this.name = name;
}
public void setBTest1(boolean bTest1) {
this.bTest1 = bTest1;
}
public void setBTest2(Boolean bTest2) {
this.bTest2 = bTest2;
}
public String toString() {
String var10000 = this.getName();
return "Demo(name=" + var10000 + ", bTest1=" + this.isBTest1() + ", bTest2=" + this.getBTest2() + ")";
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Demo)) {
return false;
} else {
Demo other = (Demo)o;
if (!other.canEqual(this)) {
return false;
} else if (this.isBTest1() != other.isBTest1()) {
return false;
} else {
Object this$bTest2 = this.getBTest2();
Object other$bTest2 = other.getBTest2();
if (this$bTest2 == null) {
if (other$bTest2 != null) {
return false;
}
} else if (!this$bTest2.equals(other$bTest2)) {
return false;
}
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Demo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
result = result * 59 + (this.isBTest1() ? 79 : 97);
Object $bTest2 = this.getBTest2();
result = result * 59 + ($bTest2 == null ? 43 : $bTest2.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public Demo() {
}
public Demo(String name, boolean bTest1, Boolean bTest2) {
this.name = name;
this.bTest1 = bTest1;
this.bTest2 = bTest2;
}
public static class DemoBuilder {
private String name;
private boolean bTest1;
private Boolean bTest2;
DemoBuilder() {
}
public DemoBuilder name(String name) {
this.name = name;
return this;
}
public DemoBuilder bTest1(boolean bTest1) {
this.bTest1 = bTest1;
return this;
}
public DemoBuilder bTest2(Boolean bTest2) {
this.bTest2 = bTest2;
return this;
}
public Demo build() {
return new Demo(this.name, this.bTest1, this.bTest2);
}
public String toString() {
return "Demo.DemoBuilder(name=" + this.name + ", bTest1=" + this.bTest1 + ", bTest2=" + this.bTest2 + ")";
}
}
}
Lombok的@Builder注解生成的 DemoBuilder构造函数如下:
就是一个java的普通对象,属性都来自我们自己写的对象,但是属性都是默认值初始化,所以我们使用new DemoBuilder().build()生成的对象,属性字段都是默认值。
Lombok提供了注解@Builder.Default,可以解决上面的问题(可跳过直接看重点截图):
package com;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@EqualsAndHashCode
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Demo {
private String name;
@Builder.Default
private boolean bTest1 = true;
@Builder.Default
private Boolean bTest2 = true;
public static void main(String[] args) {
Demo demo = new DemoBuilder().build();
System.out.println("lombok Builder 生成的对象: "+demo);
System.out.println("默认构造函数生成的对象: "+new Demo());
}
}
在属性上使用注解@Builder.Default:
我们可以下生成的java代码实现方式(可跳过直接看重点截图):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com;
import java.io.PrintStream;
public class Demo {
private String name;
private boolean bTest1;
private Boolean bTest2;
public static void main(String[] args) {
Demo demo = (new DemoBuilder()).build();
System.out.println("lombok Builder 生成的对象: " + demo);
PrintStream var10000 = System.out;
Demo var10001 = new Demo();
var10000.println("默认构造函数生成的对象: " + var10001);
}
private static boolean $default$bTest1() {
return true;
}
private static Boolean $default$bTest2() {
return true;
}
public static DemoBuilder builder() {
return new DemoBuilder();
}
public String getName() {
return this.name;
}
public boolean isBTest1() {
return this.bTest1;
}
public Boolean getBTest2() {
return this.bTest2;
}
public void setName(String name) {
this.name = name;
}
public void setBTest1(boolean bTest1) {
this.bTest1 = bTest1;
}
public void setBTest2(Boolean bTest2) {
this.bTest2 = bTest2;
}
public String toString() {
String var10000 = this.getName();
return "Demo(name=" + var10000 + ", bTest1=" + this.isBTest1() + ", bTest2=" + this.getBTest2() + ")";
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Demo)) {
return false;
} else {
Demo other = (Demo)o;
if (!other.canEqual(this)) {
return false;
} else if (this.isBTest1() != other.isBTest1()) {
return false;
} else {
Object this$bTest2 = this.getBTest2();
Object other$bTest2 = other.getBTest2();
if (this$bTest2 == null) {
if (other$bTest2 != null) {
return false;
}
} else if (!this$bTest2.equals(other$bTest2)) {
return false;
}
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Demo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
result = result * 59 + (this.isBTest1() ? 79 : 97);
Object $bTest2 = this.getBTest2();
result = result * 59 + ($bTest2 == null ? 43 : $bTest2.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public Demo() {
this.bTest1 = $default$bTest1();
this.bTest2 = $default$bTest2();
}
public Demo(String name, boolean bTest1, Boolean bTest2) {
this.name = name;
this.bTest1 = bTest1;
this.bTest2 = bTest2;
}
public static class DemoBuilder {
private String name;
private boolean bTest1$set;
private boolean bTest1$value;
private boolean bTest2$set;
private Boolean bTest2$value;
DemoBuilder() {
}
public DemoBuilder name(String name) {
this.name = name;
return this;
}
public DemoBuilder bTest1(boolean bTest1) {
this.bTest1$value = bTest1;
this.bTest1$set = true;
return this;
}
public DemoBuilder bTest2(Boolean bTest2) {
this.bTest2$value = bTest2;
this.bTest2$set = true;
return this;
}
public Demo build() {
boolean bTest1$value = this.bTest1$value;
if (!this.bTest1$set) {
bTest1$value = Demo.$default$bTest1();
}
Boolean bTest2$value = this.bTest2$value;
if (!this.bTest2$set) {
bTest2$value = Demo.$default$bTest2();
}
return new Demo(this.name, bTest1$value, bTest2$value);
}
public String toString() {
return "Demo.DemoBuilder(name=" + this.name + ", bTest1$value=" + this.bTest1$value + ", bTest2$value=" + this.bTest2$value + ")";
}
}
}
在生成的java类中,实现了两个获取属性默认值的静态方法:
生成的 DemoBuilder().build()方法,会判断是否设置了新值,如果没有,会用上面的静态方法获取赋值,从而解决了默认值的赋值问题:
虽然Lombok提供了注解@Builder.Default解决属性初始化的问题,但也带来了其他坑,且听下回分解。