不要再使用 @Builder 注解了!有深坑呀!

图片

曾经,我在《千万不要再随便使用 lombok 的 @Builder 了!》 一文中提到 @Builder 注解的其中一个大坑会导致默认值失效!

最近阅读了 《Oh !! Stop using @Builder》 发现 @Builder 的问题还不止一个,@Builder 会让人误以为是遵循构建器模式,实则不然,后面会介绍。

总的来说,不推荐再使用 @Builder 注解,接下来讲重点介绍其原因和替代方案。

二、场景复现

2.1 如果不使用 @Builder

类定义:

package io.gitrebase.demo;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class APIResponse<T> {private T payload;private Status status;}

使用示例:

package io.gitrebase.demo;import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;@Slf4j
@RestControllerAdvice(assignableTypes = io.gitrebase.demo.RestApplication.class)
public class ApplicationExceptionHandler {@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)public APIResponse handleException(Exception exception) {log.error("Unhandled Exception", exception);Status status = new Status();status.setResponseCode("RESPONSE_CODE_IDENTIFIER");status.setDescription("Bla Bla Bla");APIResponse response = new APIResponse();response.setStatus(status);return response;}}
2.2 使用 @Builder

类定义:

package io.gitrebase.demo;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class APIResponse<T> {private T payload;private Status status;}

使用示例:

package io.gitrebase.demo;import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;@Slf4j
@RestControllerAdvice(basePackageClasses = io.gitrebase.demo.RestApplication.class)
public class ApplicationExceptionHandler {@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)public APIResponse handleException(Exception exception) {log.error("Unhandled Exception", exception);return APIResponse.builder().status(Status.builder().responseCode("RESPONSE_CODE_IDENTIFIER").description("Bla Bla Bla").build()).build();}}

三、为什么不推荐使用 @Builder?

@Builder 会生成一个不完美的构建器,它不能区分哪些参数是必须的,哪些是可选的。这可能会导致构建对象时出现错误或不一致的情况。

很多人习惯于将 @Builder 和 @Data 一起使用使用会生成一个可变的构建器,它有 setter 方法可以修改构建器的状态。这违反了构建器模式的原则,即构建器应该是不可变的,一旦创建就不能修改。

@Builder 会生成一个具体类型的构建器,它不能适应不同类型的参数。这限制了构建器模式的优势,即可以根据不同的抽象类型创建不同风格的对象。

@Builder 的使用场景很有限,它只适合那些有很多参数且大部分是可选的对象。对于那些只想实现一个流式风格的对象创建,@Builder 并不是一个好的选择。

四、替代方案

4.1 首推:@Accessor

类的定义:

package io.gitrebase.demo;import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class APIResponse<T> {private T payload;private Status status;}

编译后的类:

package io.gitrebase.demo;import lombok.experimental.Accessors;@Accessors(chain = true)
public class APIResponse<T> {private T payload;private Status status;public T getPayload() {return this.payload;}public APIResponse<T> setPayload(T payload) {this.payload = payload;return this;}public Status getStatus() {return this.status;}public APIResponse<T> setStatus(Status status) {this.status = status;return this;}
}

使用示例:

package io.gitrebase.demo;import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;@Slf4j
@RestControllerAdvice(basePackageClasses = io.gitrebase.demo.RestApplication.class)
public class ApplicationExceptionHandler {@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)public APIResponse handleException(Exception exception) {log.error("Unhandled Exception", exception);var status = new Status().setResponseCode("RESPONSE_CODE_IDENTIFIER").setDescription("Bla Bla Bla");return new APIResponse().setStatus(status);}}

此外,该注解还支持一些高级方法:

/*** A container for settings for the generation of getters and setters.* <p>* Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Accessors">the project lombok features page for &#64;Accessors</a>.* <p>* Using this annotation does nothing by itself; an annotation that makes lombok generate getters and setters,* such as {@link lombok.Setter} or {@link lombok.Data} is also required.*/
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Accessors {/*** If true, accessors will be named after the field and not include a {@code get} or {@code set}* prefix. If true and {@code chain} is omitted, {@code chain} defaults to {@code true}.* <strong>default: false</strong>* * @return Whether or not to make fluent methods (named {@code fieldName()}, not for example {@code setFieldName}).*/boolean fluent() default false;/*** If true, setters return {@code this} instead of {@code void}.* <strong>default: false</strong>, unless {@code fluent=true}, then <strong>default: true</strong>* * @return Whether or not setters should return themselves (chaining) or {@code void} (no chaining).*/boolean chain() default false;/*** If present, only fields with any of the stated prefixes are given the getter/setter treatment.* Note that a prefix only counts if the next character is NOT a lowercase character or the last* letter of the prefix is not a letter (for instance an underscore). If multiple fields* all turn into the same name when the prefix is stripped, an error will be generated.* * @return If you are in the habit of prefixing your fields (for example, you name them {@code fFieldName}, specify such prefixes here).*/String[] prefix() default {};
}

另外如果一个类有些参数必传,有些参数选传,可以将必传参数定义到构造方法上,非必传参数采用 @Accessor 方式链式设置。

// 导入 lombok 注解
import lombok.Data;
import lombok.experimental.Accessors;// 定义 Person 类
@Getter // 自动生成 getter 方法
@Accessors(chain = true) // 开启链式调用
public class Person {// 定义必传的属性private String name; // 姓名private int id; // 编号// 定义选填的属性private int age; // 年龄private String address; // 地址// 定义构造函数,接收必传的参数public Person(String name, int id) {this.name = name;this.id = id;}
}// 使用示例
public class Main {public static void main(String[] args) {// 创建一个 Person 对象,传入必要的参数,通过链式调用,设置选填的属性Person person = new Person("张三", 1001).setAge(25).setAddress("北京市");// 打印 Person 对象的信息System.out.println(person);}
}
4.2 手动模拟 @Accessor

由于 @Accessor 在 lombok.experimental包下,有极个非常谨慎的人会担心未来不稳定,未来可能被移除。

其实,在我看来这个担心有些多余,目前这个注解比 @Builder 更适合使用,而且一个成熟的工具类库不会轻易移除一个功能,而且及时移除了这个功能编译期就可以感知到,替换起来也很容易。

如果真的担心不稳定或者不想依赖 lombok,那么自己在默认生成的 Setter 方法上改造一下即可。

五、启发

大多数同学使用 lombok 注解都不会主动看源码,了解有哪些高级配置。建议工作之余稍微花点时间去看一下源码。

大家在使用 lombok 注解时,一定要在脑海中能够准确“编译” 出背后的代码。如果你没有这个能力,早晚会遇到坑。如果你没有这个能力,那么多去看编译后的类,熟能生巧。

并不是大家都在用的都是对的,使用某些功能时需要主动思考是否正确,哪怕是正确的是否是最佳的。@Builder 注解的确和构建器设计模式有些背离,很多时候我们需要的是@Accessor 的行为。

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/801083.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

掌握Linux虚拟网络设备:从基础到应用的全面指南

在现代计算环境中&#xff0c;尤其是云计算☁️、容器化&#x1f4e6;和微服务架构&#x1f3d7;️大行其道的时代&#xff0c;了解和掌握Linux虚拟网络设备变得极为重要。本文将深入探讨Linux虚拟网络设备的世界&#xff0c;带你了解它们是什么、包含哪些类型、为什么需要它们…

揭秘淘宝商品详情数据接口(Taobao.item_get)

淘宝商品详情数据接口&#xff08;Taobao.item_get&#xff09;是一种允许开发者通过API访问淘宝平台上的商品详情信息的接口。通过该接口&#xff0c;开发者可以获取到商品的标题、价格、销量、描述等详细信息&#xff0c;为商品展示和销售提供数据支持。 请求示例&#xff0…

K8s学习八(配置与存储_配置)

配置与存储 配置管理 ConfigMap ConfigMap的创建 一般用于去存储 Pod 中应用所需的一些配置信息&#xff0c;或者环境变量&#xff0c;将配置于 Pod 分开&#xff0c;避免应为修改配置导致还需要重新构建 镜像与容器。configmap缩写为cmkubectl create cm -h来查看创建命令…

三星:HBM4的16层堆叠技术验证成功

随着人工智能、大数据分析、云计算及高端图形处理等领域对高速、高带宽存储需求的激增&#xff0c;下一代高带宽内存&#xff08;High Bandwidth Memory, HBM&#xff09;——HBM4已成为全球存储芯片巨头三星、SK海力士和美光竞相追逐的技术高地。 随着AI、机器学习以及高性能…

【JavaWeb】Day38.MySQL概述——数据库设计-DQL

数据库设计——DQL 介绍 DQL英文全称是Data Query Language(数据查询语言)&#xff0c;用来查询数据库表中的记录。 查询关键字&#xff1a;SELECT 查询操作是所有SQL语句当中最为常见&#xff0c;也是最为重要的操作。在一个正常的业务系统中&#xff0c;查询操作的使用频次…

kafka(四)——生产者流程分析(c++)

前言 kafka生产者负责将数据发布到kafka集群的主题&#xff1b;kafka生产者消息发送方式有两种&#xff1a; 同步发送异步回调发送 流程 流程说明&#xff1a; Kafka Producer整体可看作是一个异步处理操作&#xff1b;消息发送过程中涉及两个线程&#xff1a;main线程和se…

Java变量详解

​ 这里写目录标题 第一章、Java中的变量分类1.1&#xff09;变量分类1.2&#xff09;成员变量分类1.3&#xff09;成员变量和局部变量的区别 第二章、成员变量详解2.1&#xff09;成员变量作用域/权限修饰符2.2&#xff09;成员变量和成员属性的区别2.3&#xff09;成员变量初…

为什么 GraphQL 是构建微服务的更好选择

关于使用REST还是GraphQL来构建微服务哪个更好&#xff0c;一直存在争论。这两种技术都有其支持者和批评者&#xff0c;但当涉及微服务架构的特定需求时&#xff0c;GraphQL 成为明显的领先者。原因如下。 了解 RESTful 的关注点 虽然 REST 多年来一直是首选 API 风格&#x…

蓝桥杯 历届真题 时间显示【第十二届】【省赛】【C组】

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s #include<bits/stdc.h> #define int long long using namespace std; const int N 1e510; int n,m,t,d; int a[2][N],b[N]; //…

数据库关系模式三元及以上分解无损连接判断(表格法)

例题 1.首先构造初始表&#xff0c;如下表所示。 A B C D E ABC a1 a2 a3 b14 b15 CD b21 b22 a3 a4 b15 DE b31 b32 b33 a4 a5 2.遍历函数依赖&#xff0c;对AB→C&#xff0c;因各元组的第一、二列没有相同的分量&#xff0c;所以表不改变。 3.由C→D…

chabot项目介绍

项目介绍 整体的目录如下所示&#xff1a; 上述的项目结构中出了model是必须的外&#xff0c;其他的都可以根据训练的代码参数传入进行调整&#xff0c;有些不需要一定存在data train.pkl:对原始训练语料进行tokenize之后的文件,存储一个list对象&#xff0c;list的每条数据表…

javaWeb物流信息网的设计与实现

摘要 本文讲述了基于JSP物流信息网的设计与实现。该系统使用java语言开发&#xff0c;使系统具有更好的平台性和可扩展性。 该系统实现了用户登录、注册、查询快递信息、快递公司注册成为合作伙伴以及系统管理员对信息进行管理等功能。系统的主要界面会将所有的服务排列好&…

【java基础-高级篇十】、注解

自定义目录 一、什么是注解二、常见的注解示例三、自定义 annotation四、JDK 中的元注解五、利用反射获取注解信息六、jdk8之后注解的新特性1、可重复注解2、类型注解 一、什么是注解 加在包,类, 构造器, 方法, 成员变量, 参数, 局部变量声明上面的特殊标记就称为注解未来的开…

力扣2- 两数相加

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 …

vmware和ubuntu的问题与解决

1.问题与对策 最近使用vmware安装ubuntu16和ubuntu20&#xff0c;遇到了挺多的问题&#xff0c;如下 ubuntu在用过多次后&#xff0c;重启后登录用户名后会出现花屏的现象。 解决方案如下 在键盘上同时按键&#xff1a;Ctrl Alt F4&#xff0c;进入命令行模式&#xff0c;…

基于深度学习的电动自行车头盔佩戴检测系统

文章目录 1. 文档说明2. 运行环境说明2.1 硬件配置2.2 软件配置2.3 程序依赖库 3. 基本环境配置3.1 软件安装3.1.1 集成开发环境安装与配置3.1.2 数据库安装与配置3.1.3 编程语言安装3.1.4 CUDA和cuDNN安装与配置3.1.5 机器学习库安装 3.2 依赖库安装 4. 运行程序资源下载地 1.…

Binder通信模型

Binder是Android最主要的进程间通信方式&#xff0c;下面简单认识一下它的通信模型&#xff0c;如下图所示 服务管理进程启动时会变成上下文管理者&#xff0c;在驱动层创建一个全局的binder_node对象binder_context_mgr_node记录进程信息&#xff0c;BpServiceManager中BpBind…

HarmonyOS实战开发-如何实现跨应用数据共享实例。

介绍 本示例实现了一个跨应用数据共享实例&#xff0c;分为联系人&#xff08;数据提供方&#xff09;和联系人助手&#xff08;数据使用方&#xff09;两部分&#xff1a;联系人支持联系人数据的增、删、改、查等功能&#xff1b;联系人助手支持同步联系人数据&#xff0c;当…

血细胞检测数据集 | 用于血细胞计数+检测的小规模数据集_已经整理成VOC格式_总共410张图

项目应用场景 面向血细胞检测计数数据集&#xff0c;已经整理成 VOC 格式&#xff0c;可以直接用于目标检测算法的训练&#xff0c;如 YOLO 等目标检测算法的训练。血细胞检测数据集图片质量好&#xff0c;可直接训练出一个血细胞检测模型&#xff0c;或者作为血细胞检测数据集…

AI智能分析盒子在工地的应用,提高工地管理效率和安全性

工地ai智能分析盒子是一种基于人工智能视觉分析技术的人工智能盒子&#xff0c;旨在提升工地作业区域的管理效率和保障作业人员的安全。通过最前沿的AI视觉算法、大数据&#xff0c;能够实时监控工地现场视频流画面&#xff0c;对施工工地人员的工作着装及日常作业行为进行规范…