为什么 Lombok 的 setter 不能链式调用?—— @Accessors(chain = true) 解惑
为什么 Lombok 的 setter 不能链式调用?—— @Accessors(chain = true) 解惑
一、案例引入
在日常开发中,我们经常使用链式调用(Method Chaining)来提升代码的可读性和简洁性。例如,在构建一个业务对象并设置多个属性时:
relations.add(new ResourceRelation()
.setResourceId(resourceId)
.setBizType("course")
.setBizId(courseId));
然而,在实际编码过程中,部分开发者(我的同事就)遇到了如下编译错误或 IDEA 提示:
No candidates found for method call
或
Cannot resolve method 'setBizType' in 'void'
这表示编译器认为 setResourceId() 方法返回的是 void,无法继续调用后续方法,导致链式调用失败。
二、问题现象(看看相关实体类)
- 实体类定义(使用 Lombok)
import lombok.Data;
@Data
public class ResourceRelation {
private Long resourceId;
private String bizType;
private Long bizId;
}
- IDEA报错代码示例
// 报错:链式调用失败
relations.add(new ResourceRelation()
.setResourceId(resourceId)
.setBizType("course") // IDEA 报错:Cannot resolve method
.setBizId(courseId));
尽管使用了 @Data 注解,IDE 却提示无法链式调用 setter 方法。
三、原因分析
Lombok 的 @Data 或 @Setter 注解生成的 setter 方法默认返回 void,而非 this。
@Data 是一个组合注解,它内部包含了 @Getter、@Setter、@ToString 等。其中,@Setter 负责生成 setter 方法。
注意返回类型是 void,而非 this!
生成的等效 Java 代码如下:
public class ResourceRelation {
private Long resourceId;
private String bizType;
private Long bizId;
public void setResourceId(Long resourceId) {
this.resourceId = resourceId;
}
public void setBizType(String bizType) {
this.bizType = bizType;
}
public void setBizId(Long bizId) {
this.bizId = bizId;
}
}
由于 setResourceId() 返回 void,后续调用 .setBizType() 相当于在 void 上调用方法,语法错误,编译不通过。
四、解决方案
方案一:使用 @Accessors(chain = true)(推荐)
在实体类上添加 Lombok 的 @Accessors(chain = true) 注解,使生成的 setter 方法返回 this。
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true) // 关键:开启链式调用
public class ResourceRelation {
private Long resourceId;
private String bizType;
private Long bizId;
}
补充说明:@Accessors 的其他能力
- chain = true:返回 this,实现 setXxx().setYyy() 风格。
- fluent = true:去除 set 前缀,实现 xxx().yyy() 风格(如 .name(“Tom”).age(25))。
- 注意:chain 和 fluent 不能同时使用。
方案二:手动编写返回 this 的 setter
如果不使用 Lombok,或需精细控制,可手动实现链式 setter:
public class ResourceRelation {
private Long resourceId;
private String bizType;
private Long bizId;
public ResourceRelation setResourceId(Long resourceId) {
this.resourceId = resourceId;
return this;
}
public ResourceRelation setBizType(String bizType) {
this.bizType = bizType;
return this;
}
public ResourceRelation setBizId(Long bizId) {
this.bizId = bizId;
return this;
}
}
方案三:使用 @Builder 构建器(适合复杂对象)
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class ResourceRelation {
private Long resourceId;
private String bizType;
private Long bizId;
}
使用方式:
ResourceRelation relation = ResourceRelation.builder()
.resourceId(resourceId)
.bizType("course")
.bizId(courseId)
.build();
优点:线程安全、不可变对象支持;缺点:不适用于需要频繁修改的实例。
方案四:放弃链式,使用传统 setter
如果无法修改实体类,或项目规范禁止链式调用,可采用传统方式:
ResourceRelation relation = new ResourceRelation();
relation.setResourceId(resourceId);
relation.setBizType("course");
relation.setBizId(courseId);
relations.add(relation);
注意事项与潜在风险
使用 @Accessors(chain = true) 很方便,但它也有一些问题
违背 JavaBean 规范:
JavaBean 规范要求 setter 方法返回 void。
继承场景下链式可能中断
若父类未启用 chain = true,子类调用父类 setter 后返回父类类型,链式调用会中断
更多推荐

所有评论(0)