当前位置: 首页 > news >正文

谨慎使用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注解的缺点

  1. 如果在类上使用了@Builder 注解,那么你需要手动添加一个无参构造函数,否则有些序列化框架需要通过newInstance构造对象时会报错。
  2. 如果在类上使用了@Builder注解,就不能再在构造函数或方法上使用 @Builder注解,否则会导致重复生成构造器类
  3. 如果在类上使用了@Builder 注解,想给某个属性设置一个默认值,还需要在属性上使用@Builder.Default 注解,否则默认值会被忽略。
  4. 如果想让子类继承父类的属性,那么你需要在子类的全参构造函数上使用 @Builder注解,并且在父类上使用@AllArgsConstructor注解,否则子类的构造器类不会包含父类的属性

相关文章:

谨慎使用Lombok的@Builder注解

现在很多程序员都习惯使用Lombok来使代码更加 “简洁”。但是使用Lombok也会造成很多问题,尤其Builder 有个很大的坑,已经见过好几次由于使用Builder注解导致默认值失效的问题,如果测试时没有在意这个问题,就很容易引发线上问题。…...

leetcode455. 分发饼干 【贪心】

题目: 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j&#xff0c…...

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 云产品上使用设计器&#xff…...

Flutter问题记录 - Unable to find bundled Java version

新版本的Android Studio真的移除了JRE,jre目录找不到,怪不得报错了,不过多了一个jbr目录,找了个以前的Android Studio版本对比 搜了一下jbr(JetBrains Runtime),原来IDEA老早就开始用了&#xf…...

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种设计模式总结(二)------观察者模式 什么是装饰模式 装饰模式是一种结构型设计模式&#xff0c;它允许你在运行时为对象动态添加新的行为。该模式通过将对象放入包装器中来实现这一点&#xff0c;这个包装器会实现与…...

选择O型圈时要考虑哪些因素?

为您的应用选择正确的O型圈对于确保适当的密封和较佳性能至关重要。O型圈可用的材料和尺寸多种多样&#xff0c;做出正确的选择可能需要知道一些重要的知识点。在本文中&#xff0c;我们将讨论选择O型圈时需要考虑的一些关键因素。 1、材料兼容性&#xff1a;先要考虑的因素是…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...