lombok版本过低导致@SuperBuilder注解编译无法通过(java: 类型变量数目错误; 需要3)
lombok版本过低导致@SuperBuilder注解编译无法通过
- 项目背景
- 特殊现象描述
- 分析
- 解决方案
项目背景
由于博主在开发过程中,需要让子类可以调用父类的属性进行builder构造实例对象,因此将lombok的@Builder
注解提升为了@SuperBuilder
注解。
在修改后的父类代码如下:
package com.yc.utils.common.apiresp;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;import java.util.List;/**** 约定:1,继承者需使用 @SuperBuilder 提供构造者模式;* 2,继承者需使用 @NoArgsConstructor 提供无参构造(若子类需要参与框架反射)**/@SuperBuilder // 放弃 @Builder,使用 @SuperBuilder
@NoArgsConstructor
@Data
@ApiModel(description = "公共分页信息响应")
public abstract class BasePageInfoResponse<T> {@ApiModelProperty(notes = "每页条数", example = "10")private Integer pageSize;@ApiModelProperty(notes = "当前页码", example = "1")private Integer pageNum;@ApiModelProperty(notes = "总页数", example = "10")private Integer pageCount;@ApiModelProperty(notes = "总记录数", example = "100")private Long total;@ApiModelProperty(notes = "实际记录数", example = "100")@Builder.Defaultprivate Long realTotal = 0L;// 使用泛型来抽象具体的数据对象类型 @ApiModelProperty(notes = "当前页记录列表", example = "")private List<T> items;}
子类代码如下:
package com.yc.dompet.controller.results.connect;import com.yc.dompet.dto.result.BalanceDataDTO;
import com.yc.utils.common.apiresp.BasePageInfoResponse;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;@SuperBuilder // 由于需要调用父类的属性参与构造者模式,因此不能使用 @Builder,改用 @SuperBuilder
@NoArgsConstructor
@Data
@ApiModel(description = "平账管理列表-响应数据")
public class GetBalanceDataResponse extends BasePageInfoResponse<BalanceDataDTO> { // 泛型指向的是具体的外部类型
}
通过这种父子类的设计,是可以做到正常使用的。且项目运行正常,效果达到了初步的预期。
特殊现象描述
然而,在开发过程中,有个特殊的需求实现,需要让父类的泛型指定为子类的内部静态类对象。
实现的子类代码如下:
package com.yc.dompet.controller.results.connect;import com.yc.utils.common.apiresp.BasePageInfoResponse;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;@SuperBuilder
@NoArgsConstructor
@Data
@ApiModel(description = "放款详情列表-响应数据")
public class GetLoanDetailResponse extends BasePageInfoResponse<GetLoanDetailResponse.LoanDetailResult> { // 父类的泛型具体指向了子类的静态内部类// 子类内部定义了静态内部类@Data@NoArgsConstructor@AllArgsConstructor@ToString@ApiModel(description = "放款订单查询结果")public static class LoanDetailResult {/*** 订单ID*/@ApiModelProperty(value = "订单ID")private String appOrderId;/*** 用户ID*/@ApiModelProperty(value = "用户ID")private String userId;/*** 产品编码*/@ApiModelProperty(value = "产品编码")private String productCode;/*** 电话号码*/@ApiModelProperty(value = "电话号码")private String phone;/*** 放款状态:2-放款中,4-放款成功,5-放款失败,6-已结清,7-关单,23-放款取消,-1已注销*/@ApiModelProperty(value = "放款状态:2-放款中,4-放款成功,5-放款失败,6-已结清,7-关单,23-放款取消,-1已注销")private String loanStatus;}}
经博主这么一修改,理论上来说,这种改动应该是符合Java语法要求的。但在编译项目时,却直接抛出了异常:
/.../controller/results/connect/GetLoanDetailResponse.java:19 java: 类型变量数目错误; 需要3
分析
类型变量数目错误 —— 类型变量就是指的泛型,那就是泛型数目匹配不上。指向的具体位置就是刚刚改动的子类的位置,而子类的代码中就只有一个涉及到了泛型,那就是继承父类时。
那么编译器为什么会说这个改动后的子类需要3个泛型实现呢?
百思不得其解,一开始博主还曾怀疑过是这种子类内部类指定为父类泛型具体类型的写法不符合语法规范呢。
但这种写法博主也不是第一次写,之前也没有过这种奇怪的报错,所以打消了这个思路。
于是博主把思路转为了在lombok的@SuperBuilder
注解上,因为之前博主使用子类内部类指定为父类泛型具体类型这种写法时,并没有与@SuperBuilder
一同使用,当时是没有编译失败问题的。
查看继承了同一个父类的其他子类(没有使用内部类指定为父类泛型)编译后生成的class文件,结果发现确实是 @SuperBuilder
模式下会导致子类需要有3个泛型变量。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.yc.dompet.controller.results.connect;import com.yc.dompet.dto.result.BalanceDataDTO;
import com.yc.utils.common.apiresp.BasePageInfoResponse;
import io.swagger.annotations.ApiModel;@ApiModel(description = "平账管理列表-响应数据"
)
public class GetBalanceDataResponse extends BasePageInfoResponse<BalanceDataDTO> {protected GetBalanceDataResponse(final GetBalanceDataResponseBuilder<?, ?> b) {super(b);}// 此处的 builder 需要 2 个泛型类型public static GetBalanceDataResponseBuilder<?, ?> builder() {return new GetBalanceDataResponseBuilderImpl();}public GetBalanceDataResponse() {}public boolean equals(final Object o) {if (o == this) {return true;} else if (!(o instanceof GetBalanceDataResponse)) {return false;} else {GetBalanceDataResponse other = (GetBalanceDataResponse)o;return other.canEqual(this);}}protected boolean canEqual(final Object other) {return other instanceof GetBalanceDataResponse;}public int hashCode() {int result = true;return 1;}public String toString() {return "GetBalanceDataResponse()";}private static final class GetBalanceDataResponseBuilderImpl extends GetBalanceDataResponseBuilder<GetBalanceDataResponse, GetBalanceDataResponseBuilderImpl> {private GetBalanceDataResponseBuilderImpl() {}protected GetBalanceDataResponseBuilderImpl self() {return this;}public GetBalanceDataResponse build() {return new GetBalanceDataResponse(this);}}// 此处我们最终使用的 GetBalanceDataResponseBuilder 一共是需要 3 个泛型类型public abstract static class GetBalanceDataResponseBuilder<C extends GetBalanceDataResponse, B extends GetBalanceDataResponseBuilder<C, B>> extends BasePageInfoResponse.BasePageInfoResponseBuilder<BalanceDataDTO, C, B> {public GetBalanceDataResponseBuilder() {}protected abstract B self();public abstract C build();public String toString() {return "GetBalanceDataResponse.GetBalanceDataResponseBuilder(super=" + super.toString() + ")";}}
}
因此确定了引起本次意外的源头是 @SuperBuilder
,但是为什么是泛型执行了内部类时才会报该编译异常呢?
经过博主多次实验无果后,突然灵机一动,想到博主在维护的是一个老旧项目,使用的springboot和lombok版本都挺老旧, @SuperBuilder
作为一个新出的注解,可能老版本对该注解的支持度不是很好。
于是博主将父子类相应的java文件拷贝到一个重新创建的新项目中,在项目中引入了较新的lombok项目,神了,项目居然能跑起来了!!!
解决方案
最终定下来的解决方案就是在使用到 @SuperBuilder
的模块中单独引入了一个新版本的lombok,来解决编译失败的问题。
旧版本:
<!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.4</version>
</dependency>
新版本:
<!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version>
</dependency>
程序员开发过程中天不怕地不怕,就怕遇到这种因为版本问题而不自知,疯狂怀疑自己代码有问题的异常现象,版本问题你害人不浅呐!
😅 😮💨 🤬
好了,以上就是我个人对本次内容的理解与解析,如果有什么不恰当的地方,还望各位兄弟在评论区指出哦。
如果这篇文章对你有帮助的话,不妨点个关注吧~
期待下次我们共同讨论,一起进步~