背景
之前我定义类时,一般不喜欢使用@Builder注解,但组内同事很热衷于使用这个,于是我也抱着跟上新技术的步伐,对DTO对象类添加了@Builder注解。
当测试验证时,出现了异常:
java.lang.InstantiationException: com.ctrip.flight.backendservice.flowblock.orderpolicy.web.soa.PassengerBuilder
at java.lang.Class.newInstance(Class.java:427)
at com.ctrip.flight.backendservice.flowblock.orderpolicy.web.soa.NewBuilderTest.testBuilderNewInstance(NewBuilderTest.java:18)
Caused by: java.lang.NoSuchMethodException: com.ctrip.flight.backendservice.flowblock.orderpolicy.web.soa.PassengerBuilder.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more
纳尼,竟然会报错NosuchMethodException???
回归代码
先看下测试代码
@Test
public void testBuilderNewInstance() throws InstantiationException, IllegalAccessException {
Class<PassengerBuilder> newBuilderClass = PassengerBuilder.class;
System.out.println(newBuilderClass.newInstance());
}
定义的PassengerBuilder类
@Builder
public class PassengerBuilder {
private String name;
private int age;
}
再看下生成的.class文件
**根本原因:**在生成的PassengerBuilder.class文件中没有newInstance,所以报错了
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ctrip.flight.backendservice.flowblock.orderpolicy.web.soa;
public class PassengerBuilder {
private String name;
private int age;
PassengerBuilder(String name, int age) {
this.name = name;
this.age = age;
}
public static PassengerBuilderBuilder builder() {
return new PassengerBuilderBuilder();
}
public static class PassengerBuilderBuilder {
private String name;
private int age;
PassengerBuilderBuilder() {
}
public PassengerBuilderBuilder name(String name) {
this.name = name;
return this;
}
public PassengerBuilderBuilder age(int age) {
this.age = age;
return this;
}
public PassengerBuilder build() {
return new PassengerBuilder(this.name, this.age);
}
public String toString() {
return "PassengerBuilder.PassengerBuilderBuilder(name=" + this.name + ", age=" + this.age + ")";
}
}
}
解决方案
在类上使用@Builder时,同时加上另外两个注解,完美解决问题。
@NoArgsConstructor
@AllArgsConstructor
加上这两个注解后,编译类后会生成无参构造函数和有参构造函数
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ctrip.flight.backendservice.flowblock.orderpolicy.web.soa;
public class PassengerBuilder {
private String name;
private int age;
public static PassengerBuilderBuilder builder() {
return new PassengerBuilderBuilder();
}
public PassengerBuilder() {
}
public PassengerBuilder(String name, int age) {
this.name = name;
this.age = age;
}
public static class PassengerBuilderBuilder {
private String name;
private int age;
PassengerBuilderBuilder() {
}
public PassengerBuilderBuilder name(String name) {
this.name = name;
return this;
}
public PassengerBuilderBuilder age(int age) {
this.age = age;
return this;
}
public PassengerBuilder build() {
return new PassengerBuilder(this.name, this.age);
}
public String toString() {
return "PassengerBuilder.PassengerBuilderBuilder(name=" + this.name + ", age=" + this.age + ")";
}
}
}
总结@Builder的坑
-
在类上使用了 @Builder 注解,那么需要手动添加一个无参构造函数,否则有些序列化框架需要通过 newInstance 构造对象时会报错。
【正如本文上述分析】 -
在类上使用了 @Builder 注解,那么不能再在构造函数或方法上使用 @Builder 注解,否则会导致重复生成构造器类。
-
给某个属性设置一个默认值,那么需要在属性上使用 @Builder.Default 注解,否则默认值会被忽略,无法达到设置默认值的功能。
-
如果想让子类继承父类的属性,那么需要在子类的全参构造函数上使用 @Builder 注解,并且在父类上使用 @AllArgsConstructor 注解,否则子类的构造器类不会包含父类的属性。