谨慎使用Lombok的@Builder注解
现在很多程序员都习惯使用Lombok来使代码更加 “简洁”。但是使用Lombok也会造成很多问题,尤其@Builder 有个很大的坑,已经见过好几次由于使用@Builder注解导致默认值失效的问题,如果测试时没有在意这个问题,就很容易引发线上问题。
问题复现
我们随便定义一个类Config,对其中两个属性设置默认值。
import lombok.Builder;
import lombok.Data;@Data
@Builder
public class Config {private boolean isOpen = true;private String name;private int value = 20;
}public class LombokDemo {public static void main(String[] args) {Config config = Config.builder().name("test").build();System.out.println(config);}
}
借助Builder模式创建Config类实例时,仅设置name属性,然后打印出实例
public class LombokDemo {public static void main(String[] args) {Config config = Config.builder().name("test").build();System.out.println(config);}
}
输出结果如下。
Config(isOpen=false, name=test, value=0)
我们为isOpen及value属性设置的默认值失效了。
原因分析
想了解为什么会这样,我们只需要查看使用Lombok的注解后的Config类的 class文件长啥样就明白了。@Builder通过Lombok的注解处理器,在编译时会自动生成一个静态内部类,这个内部类就是所谓的builder类,它包含了和被注解的类中的属性一一对应的setter方法,并且在build()方法中返回一个被注解的类的对象。这个builder类的代码实现是通过Lombok生成的,所以我们不需要手动编写。
public class Config {private boolean isOpen = true;private String name;private int value = 20;Config(boolean isOpen, String name, int value) {this.isOpen = isOpen;this.name = name;this.value = value;}public static ConfigBuilder builder() {return new ConfigBuilder();}public boolean isOpen() {return this.isOpen;}public String getName() {return this.name;}public int getValue() {return this.value;}public void setOpen(boolean isOpen) {this.isOpen = isOpen;}public void setName(String name) {this.name = name;}public void setValue(int value) {this.value = value;}public boolean equals(Object o) {// 省略}protected boolean canEqual(Object other) {return other instanceof Config;}public int hashCode() {// 省略}public String toString() {return "Config(isOpen=" + this.isOpen() + ", name=" + this.getName() + ", value=" + this.getValue() + ")";}public static class ConfigBuilder {private boolean isOpen;private String name;private int value;ConfigBuilder() {}public ConfigBuilder isOpen(boolean isOpen) {this.isOpen = isOpen;return this;}public ConfigBuilder name(String name) {this.name = name;return this;}public ConfigBuilder value(int value) {this.value = value;return this;}public Config build() {return new Config(this.isOpen, this.name, this.value);}public String toString() {return "Config.ConfigBuilder(isOpen=" + this.isOpen + ", name=" + this.name + ", value=" + this.value + ")";}}
}
可以看到,ConfigBuilder中isOpen和value属性并没有使用我们想要设置的默认值。调用build方法时, ConfigBuilder会调用全参的构造方法来构造Config 对象。
解决方法
使用@Builder.Default注解来标识带默认值的属性
import lombok.Builder;
import lombok.Data;@Data
@Builder
public class SomeConfig {@Builder.Defaultprivate boolean isOpen = true;private String name;@Builder.Defaultprivate int value = 20;
}
修改后输出结果如下
Config(isOpen=true, name=test, value=20)
为什么加了@Builder.Default注解就能解决问题呢,看一下编译后的class文件就明白了
public class Config {private boolean isOpen;private String name;private int value;private static boolean $default$isOpen() {return true;}private static int $default$value() {return 20;}Config(boolean isOpen, String name, int value) {this.isOpen = isOpen;this.name = name;this.value = value;}public static ConfigBuilder builder() {return new ConfigBuilder();}public boolean isOpen() {return this.isOpen;}public String getName() {return this.name;}public int getValue() {return this.value;}public void setOpen(boolean isOpen) {this.isOpen = isOpen;}public void setName(String name) {this.name = name;}public void setValue(int value) {this.value = value;}public boolean equals(Object o) {// 省略}protected boolean canEqual(Object other) {return other instanceof Config;}public int hashCode() {// 省略}public String toString() {boolean var10000 = this.isOpen();return "Config(isOpen=" + var10000 + ", name=" + this.getName() + ", value=" + this.getValue() + ")";}public static class ConfigBuilder {private boolean isOpen$set;private boolean isOpen$value;private String name;private boolean value$set;private int value$value;ConfigBuilder() {}public ConfigBuilder isOpen(boolean isOpen) {this.isOpen$value = isOpen;this.isOpen$set = true;return this;}public ConfigBuilder name(String name) {this.name = name;return this;}public ConfigBuilder value(int value) {this.value$value = value;this.value$set = true;return this;}public Config build() {boolean isOpen$value = this.isOpen$value;if (!this.isOpen$set) {isOpen$value = Config.$default$isOpen();}int value$value = this.value$value;if (!this.value$set) {value$value = Config.$default$value();}return new Config(isOpen$value, this.name, value$value);}public String toString() {return "Config.ConfigBuilder(isOpen$value=" + this.isOpen$value + ", name=" + this.name + ", value$value=" + this.value$value + ")";}}
}
每个设置默认值的属性都会在Builder中加上是否设置的标记,如果没有主动设置值,则调用Config中的默认值的静态方法进行赋值,然后再调用Config全参构造方法构造该对象。
使用@Builder注解的缺点
- 如果在类上使用了@Builder 注解,那么你需要手动添加一个无参构造函数,否则有些序列化框架需要通过newInstance构造对象时会报错。
- 如果在类上使用了@Builder注解,就不能再在构造函数或方法上使用 @Builder注解,否则会导致重复生成构造器类
- 如果在类上使用了@Builder 注解,想给某个属性设置一个默认值,还需要在属性上使用@Builder.Default 注解,否则默认值会被忽略。
- 如果想让子类继承父类的属性,那么你需要在子类的全参构造函数上使用 @Builder注解,并且在父类上使用@AllArgsConstructor注解,否则子类的构造器类不会包含父类的属性
相关文章:
谨慎使用Lombok的@Builder注解
现在很多程序员都习惯使用Lombok来使代码更加 “简洁”。但是使用Lombok也会造成很多问题,尤其Builder 有个很大的坑,已经见过好几次由于使用Builder注解导致默认值失效的问题,如果测试时没有在意这个问题,就很容易引发线上问题。…...
leetcode455. 分发饼干 【贪心】
题目: 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,…...
4V-28V Vin,6A同步降压DCDC变换器,集成3.3V和150mA LDO——SCT2361FPBR
SCT2361是一种高效率的同步降压型DC-DC变换器,集成3.3V和150mA LDO。输入电压范围为4V-28V,输出电压可调为0.6V,具有3mmx3mm的小QFN封装,可提供连续6A的输出电流。该器件将高、低压侧功率mosfet集成,使导通损耗降到最低…...
Linux中的scp指令
在Linux和Unix系统中,scp(Secure Copy Protocol)是一个用于通过SSH协议进行安全文件传输的命令行实用程序。与传统的cp(copy)命令不同,scp允许用户在不同的机器之间、或同一台机器的不同位置之间传输文件或…...
剑指 Offer 11. 旋转数组的最小数字
剑指 Offer 11. 旋转数组的最小数字 二分 要注意的是,由于存在重复数字,所以初始状态可能不满足二分的性质。不满足的情况是:左边开始的数字和右边结束的数字相等,所以一开始要缩小右边界,让右边界的数字小于第一个数…...
Redis面试题总结
1.什么是Redis Redis 是一种基于内存的数据库对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景,比如 String(字符串)、Hash(哈希)、…...
【Eclipse】搭建python环境;运行第一个python程序helloword
目录 0.环境 1.需准备&搭建思路 2.搭建具体步骤 1)查看是否安装过python 2)安装eclipse 3)安装和配置pyDev 3.创建第一个python程序具体步骤 1)新建项目 2)输入项目名字,和配置选项 3&#x…...
OpenAI 发布企业版ChatGPT-4
OpenAI 发布企业版ChatGPT-4 ChatGPT Enterprise 版本功能ChatGPT Enterprise 对比ChatGPT Enterprise 不同点未来发布计划OpenAI 发布企业版ChatGPT-4 OpenAI 宣布,鉴于ChatGPT的爆炸性成果,推出了针对企业的 ChatGPT Enterprise 版 ChatGPT Enterprise 版本功能 包含所有…...
Flowable7 设计器
1、flowable7 已经在主版本上移除了Flowable UI相关的包,包含bpm-json相关的所有包和流程设计器相关前端文件。 2、flowable7 版本目前只保留了xml运行相关的包,ui modeler已经移除 3、目前官方给的回复是只能在 flowable 云产品上使用设计器ÿ…...
Flutter问题记录 - Unable to find bundled Java version
新版本的Android Studio真的移除了JRE,jre目录找不到,怪不得报错了,不过多了一个jbr目录,找了个以前的Android Studio版本对比 搜了一下jbr(JetBrains Runtime),原来IDEA老早就开始用了…...
Tomcat 日志乱码问题解决
我就是三井,一个永不放弃希望的男人。——《灌篮高手》 Tomcat 日志乱码问题解决 乱码原因:字符编码不一致 如:国内电脑一般都是GBK编码,而Tomcat日志使用的是UTF-8编码 解决方法:将对应字符编码由 UTF-8 改为 GBK 即…...
yum源以及rpm安装包配置、yum源冲突、yum-config-manager命令找不到、curl: (35)、docker镜像重复拉取失败
yum源配置并解决冲突、curl: (35)、docker镜像重复拉取失败、yum-config-manager命令找不到的解决方法 有的时候按照教程走,可能会设置yum源,设置后用yum下载东西很有可能或造成冲突 yum源冲突的解决方式无非有两种:1. 删除冲突软…...
ChatGPT和文心一言的优缺点比较
ChatGPT和文心一言都是自然语言生成技术的代表,下面是它们的优缺点比较: ChatGPT的优点: 自由度高:ChatGPT生成的文本与给定的话题没有紧密的关联,可以灵活地生成多种不同的文本。多样性高:ChatGPT可以生…...
⛳ 面试题-单例模式会存在线程安全问题吗?
🎍目录 ⛳ 面试题-单例模式会存在线程安全问题吗?🎨 一、单例模式-简介🚜 二、饿汉式🐾 三、懒汉式🎯 3.1、懒汉式:在调用 getInstance 的时候才创建对象。(线程不安全)&…...
C - 滑动窗口 /【模板】单调队列
Description 有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。 例如: The array is [1,3,−1,−3,5,3,6,7] and k3。 Input 输入一共有…...
工厂人员作业行为动作识别检测算法
工厂人员作业行为动作识别检测算法通过yolov7python深度学习算法框架模型,工厂人员作业行为动作识别检测算法实时识别并分析现场人员操作动作行为是否符合SOP安全规范流程作业标准,如果不符合则立即抓拍告警提醒。Python是一种由Guido van Rossum开发的通…...
【数据结构】顺序表详解
当我们写完通讯录后,顺序表肯定难不倒你,跟着小张一起来学习顺序表吧! 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表&#x…...
HTML 播放器效果
效果图 实现代码 <!DOCTYPE HTML> <html><head><title>爱看动漫社区 | 首页 </title><link href"css/bootstrap.css" relstylesheet typetext/css /><!-- jQuery --><script src"js/jquery-1.11.0.min.js"…...
C++常用23种设计模式总结(三)------装饰模式
往期回顾 C常用23种设计模式总结(一)------单例模式 C常用23种设计模式总结(二)------观察者模式 什么是装饰模式 装饰模式是一种结构型设计模式,它允许你在运行时为对象动态添加新的行为。该模式通过将对象放入包装器中来实现这一点,这个包装器会实现与…...
选择O型圈时要考虑哪些因素?
为您的应用选择正确的O型圈对于确保适当的密封和较佳性能至关重要。O型圈可用的材料和尺寸多种多样,做出正确的选择可能需要知道一些重要的知识点。在本文中,我们将讨论选择O型圈时需要考虑的一些关键因素。 1、材料兼容性:先要考虑的因素是…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
