场景
在项目开发中,需要基于某个类进行一些字段的拓展,那么自然而然想到的做法是extends该类。而两个类都需要使用@Builder进行构造。代码如下:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EmployeeDto {
private String companyId;
private String jobNo;
private Integer gender;
private Integer age;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class HardwareEmployeeDto extends EmployeeDto{
private String title;
}
使用时:
@GetMapping(value = "/v1/builder/test")
public void testBuilder(){
HardwareEmployeeDto hardwareEmployeeDto = HardwareEmployeeDto.builder().build();
}
问题
本来想现在直接build一个子类HardwareEmployeeDto,同时也需要给父类赋值,但是此时无法对父类的字段进行赋值,只能给子类的title字段赋值。
就算只赋值title,编译也会报错
@GetMapping(value = "/v1/builder/test")
public void testBuilder(){
HardwareEmployeeDto hardwareEmployeeDto = HardwareEmployeeDto.builder().title("Hardware Department").build();
System.out.println(hardwareEmployeeDto);
}
这个报错是说明子类尝试申明具有跟父类相同名称的builder,默认的构造器都叫builder(),此时可以给子类的构造器自定义一个新名字,使用子类的构造器进行构造。
@Data
@Builder(builderMethodName = "hardwareEmployeeBuilder")
@AllArgsConstructor
@NoArgsConstructor
public class HardwareEmployeeDto extends EmployeeDto{
private String title;
}
@GetMapping(value = "/v1/builder/test")
public void testBuilder(){
HardwareEmployeeDto hardwareEmployeeDto = HardwareEmployeeDto.hardwareEmployeeBuilder().title("Hardware Department").build();
System.out.println(hardwareEmployeeDto);
}
输出内容
HardwareEmployeeDto(title=Hardware Department)
这样是可以编译通过,正常运行,但是还是无法给父类赋值
解决
- 子类增加构造函数,通过构造函数给父类字段赋值,并且使用@Builder注解自定义构造器名字
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HardwareEmployeeDto extends EmployeeDto{
@Builder(builderMethodName = "hardwareEmployeeBuilder")
public HardwareEmployeeDto(String companyId, String jobNo, Integer gender, Integer age, String title) {
super(companyId, jobNo, gender, age);
this.title = title;
}
private String title;
}
@GetMapping(value = "/v1/builder/test")
public void testBuilder(){
HardwareEmployeeDto hardwareEmployeeDto = HardwareEmployeeDto.hardwareEmployeeBuilder()
.title("Hardware Department")
.companyId("888")
.age(18)
.gender(0)
.jobNo("1")
.build();
System.out.println("jobNo is : " + hardwareEmployeeDto.getJobNo());
}
- 换成使用@SuperBuilder注解
将子类和父类都换成@SuperBuilder注解,子类也不必指定构造器名称。
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class EmployeeDto {
private String companyId;
private String jobNo;
private Integer gender;
private Integer age;
}
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class HardwareEmployeeDto extends EmployeeDto{
private String title;
}
@GetMapping(value = "/v1/builder/test")
public void testBuilder(){
HardwareEmployeeDto hardwareEmployeeDto = HardwareEmployeeDto.builder()
.title("Hardware Department")
.companyId("888")
.age(18)
.gender(0)
.jobNo("1")
.build();
System.out.println("jobNo is : " + hardwareEmployeeDto.getJobNo());
}
注意
- @SuperBuilder和@Builder在父类和子类中不能混用
- @SuperBuilder在所有的父类和子类中都必须使用上,缺一不可。
另:@SuperBuilder(toBuilder = true)用法
@SuperBuilder的toBuilder默认是false,如果置为true,需要所有的父类也都设置该字段为true。设置了true之后,所有的类的实例会有toBuilder方法,是一个深拷贝的方法,不会覆盖原来的实例的值,而是生成一个新的实例对象赋上新值。
@Data
@SuperBuilder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
public class EmployeeDto {
private String companyId;
private String jobNo;
private Integer gender;
private Integer age;
}
@Data
@SuperBuilder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
public class HardwareEmployeeDto extends EmployeeDto{
private String title;
}
@GetMapping(value = "/v1/builder/test")
public void testBuilder(){
HardwareEmployeeDto hardwareEmployeeDto = HardwareEmployeeDto.builder()
.title("Hardware Department")
.companyId("888")
.age(18)
.gender(0)
.jobNo("1")
.build();
HardwareEmployeeDto build = hardwareEmployeeDto.toBuilder().jobNo("2").age(28).build();
System.out.println("jobNo is : " + hardwareEmployeeDto.getJobNo());
System.out.println("jobNo is : " + build.getJobNo());
}
输出:
jobNo is : 1
jobNo is : 2