踩坑!Lombok @Builder和@NoArgsConstructor一起用,默认值居然失效了 📅 发布时间:2026/7/17 11:51:10 👁️ 浏览次数: 踩坑Lombok Builder和NoArgsConstructor一起用默认值居然失效了前几天维护一个老接口新服务对接的时候突然报空指针异常排查了半天最后发现居然是Lombok注解用错了导致的。说真的Lombok这东西平时用着是真方便Data、Builder一套注解甩上去getter、setter、构造器全不用自己写省了好多代码。但也正因为太方便有时候忽略了注解之间的搭配问题很容易踩坑。这次踩的坑就是Builder、NoArgsConstructor、AllArgsConstructor和Data一起用导致DTO里的默认值失效进而引发了空指针。今天就把这个坑整理出来看看有没有朋友和我一样也栽过这个跟头。先上我最开始写的DTO代码看着其实没什么问题importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;DataBuilderNoArgsConstructorAllArgsConstructorpublicclassReqDto{// 给field1设置了默认值trueprivateBooleanfield1true;privateStringfield2;privateStringfield3;}接口里的判断逻辑也很简单就是用field1作为条件之一判断field2和field3不为空publicvoiddoBusiness(ReqDtoreqDto){// 这里报了空指针reqDto.getField1()居然是nullif(reqDto.getField1()reqDto.getField2()!nullreqDto.getField3()!null){// 执行业务逻辑System.out.println(业务逻辑执行成功);}}当时看到空指针报错我第一反应是新服务对接时没给field1传值。但找对接的同事确认后他们说field1是可选参数没传的时候就用默认值。我就纳闷了我明明给field1设置了默认值true就算没传值也应该是true才对怎么会是null呢后来我把DTO的class文件反编译了一下看完瞬间就明白了问题出在Lombok的Builder注解上。先给大家看一下上面那套注解反编译后的DTO代码是什么样的关键部分截取publicclassReqDto{privateBooleanfield1true;privateStringfield2;privateStringfield3;// NoArgsConstructor生成的无参构造器publicReqDto(){}// AllArgsConstructor生成的全参构造器publicReqDto(Booleanfield1,Stringfield2,Stringfield3){this.field1field1;this.field2field2;this.field3field3;}// Builder生成的builder内部类和相关方法publicstaticReqDto.ReqDtoBuilderbuilder(){returnnewReqDto.ReqDtoBuilder();}publicstaticclassReqDtoBuilder{privateBooleanfield1;privateStringfield2;privateStringfield3;ReqDtoBuilder(){}publicReqDto.ReqDtoBuilderfield1(Booleanfield1){this.field1field1;returnthis;}publicReqDto.ReqDtoBuilderfield2(Stringfield2){this.field2field2;returnthis;}publicReqDto.ReqDtoBuilderfield3(Stringfield3){this.field3field3;returnthis;}publicReqDtobuild(){returnnewReqDto(this.field1,this.field2,this.field3);}}// 下面是Data生成的getter、setter方法省略...}重点看builder的build方法它调用的是全参构造器new ReqDto(this.field1, this.field2, this.field3)。而builder内部类里的field1是没有设置默认值的默认就是null。当我们用builder创建对象又没有给field1赋值的时候builder里的field1就是null然后通过全参构造器传给DTO的field1直接覆盖了我们在DTO里设置的默认值true。这就是问题的根源我们以为给field1设置了默认值就万事大吉但Builder注解生成的代码直接把这个默认值给“覆盖”掉了。举个例子当我们用builder创建对象只传field2和field3不给field1传值ReqDtoreqDtoReqDto.builder().field2(test2).field3(test3).build();这时reqDto.getField1()的值不是我们预期的true而是null。接口里用这个null去做判断自然就报空指针了。找到问题之后解决办法就很简单了有两种常用的方式根据自己的场景选就行。第一种方式给Builder的field设置默认值推荐不用改其他注解直接在Builder注解里给需要默认值的字段设置默认值这样builder创建对象时就算不赋值也会用我们设置的默认值。importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;Data// 给field1设置默认值true覆盖builder内部的null默认值Builder(field1true)NoArgsConstructorAllArgsConstructorpublicclassReqDto{privateBooleanfield1true;privateStringfield2;privateStringfield3;}这样修改后再用builder创建对象不给field1赋值field1就会是true和我们预期的一致。第二种方式不用AllArgsConstructor手动写全参构造器灵活度高如果不想用Builder的field默认值设置也可以去掉AllArgsConstructor注解自己手动写全参构造器在构造器里给field1设置默认值。importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;DataBuilderNoArgsConstructor// 去掉AllArgsConstructor手动写全参构造器publicclassReqDto{privateBooleanfield1true;privateStringfield2;privateStringfield3;// 手动写全参构造器给field1设置默认值publicReqDto(Booleanfield1,Stringfield2,Stringfield3){this.field1field1null?true:field1;this.field2field2;this.field3field3;}}这种方式的好处是就算field1传了null也能通过构造器的判断把它设为true避免空指针。这里还有个小细节很多人可能会忽略如果去掉AllArgsConstructor只保留Builder和NoArgsConstructorLombok会自动生成一个全参构造器吗答案是不会。Builder注解本身不会生成全参构造器它只是依赖全参构造器来创建对象。如果我们不手动写也不用AllArgsConstructor编译的时候就会报错提示找不到全参构造器。再补充一个点为什么我们给DTO的field1设置了默认值还是会被覆盖因为Java的赋值顺序是先执行字段的默认值赋值再执行构造器里的赋值。我们用builder创建对象时调用的是全参构造器构造器里的this.field1 field1builder里的null会覆盖掉字段本身的默认值true。就像下面这段简单的代码执行完之后field1的值是null而不是truepublicclassTest{privateBooleanfield1true;publicTest(Booleanfield1){this.field1field1;}publicstaticvoidmain(String[]args){TesttestnewTest(null);System.out.println(test.field1);// 输出null}}道理是一样的Lombok生成的代码本质上也是这样的逻辑只是我们平时看不到而已。最后再总结一下这个坑用大白话讲就是当Builder和NoArgsConstructor、AllArgsConstructor一起使用时Builder生成的builder类其内部字段默认是nullbuild方法会调用全参构造器用builder里的null覆盖DTO字段的默认值导致默认值失效。解决办法就两种要么用Builder(field 默认值)给字段设置默认值要么手动写全参构造器在构造器里处理默认值。其实Lombok的坑还有不少但大多都是因为对注解的底层实现不了解盲目搭配使用导致的。平时用的时候多留意一下注解之间的兼容性必要的时候反编译看一下生成的代码就能避免很多不必要的麻烦。希望我这个踩坑经历能帮大家避开这个Lombok的小陷阱以后写代码少走点弯路
为什么GEO优化是新的内容战略核心?尹邦奇三层结构模型深度解读 在AI生成式搜索(如ChatGPT、Google SGE、DeepSeek等)迅速普及的当下,传统SEO已不能完全满足品牌在新一代搜索生态中的可见性需求。生成式引擎优化正成为内容策略与品牌曝光的新标配。业内专家尹邦奇在其《GEO优化白皮书:生成式搜索… 2026/7/17 11:50:56
Java springboot基于微信小程序的医院问诊预约系统(源码+文档+运行视频+讲解视频) 文章目录 系列文章目录目的前言一、详细视频演示二、项目部分实现截图三、技术栈 后端框架springboot前端框架vue持久层框架MyBaitsPlus微信小程序介绍系统测试 四、代码参考 源码获取 目的 在医疗资源日益紧张的背景下,医院问诊预约系统成为缓解看病难问题的有效… 2026/7/17 3:55:32
别再瞎找了!8个AI论文平台测评:专科生毕业论文+开题报告全攻略 在当前高校教育不断深化的背景下,专科生的学术写作需求日益增长,但面对毕业论文和开题报告的撰写任务,许多学生仍感到无从下手。尤其是对于时间紧张、经验不足的学生来说,如何高效完成高质量的学术内容成为一大难题。为此… 2026/7/6 20:04:24
Fable 5 的 7 月 19 日不是发布延期:模型可用性正在变成动态资源(九维可用性 + SLO 重构) TL;DR(三行:场景/结论/产出) 场景:AI 应用开发者把 “Fable 5 延期到 7 月 19 日” 当作模型发布延期,影响生产系统能力契约设计结论:Fable 5 已于 2026-06-09 正式发布(模型 ID claude-fable-5);7 月 19 日延长的是付费套餐访问窗口和 Claude Code 提高 50% 后的周限额,不是模… 2026/7/17 11:47:22
2026届秋招薪资表曝光:国企vs大厂真相揭秘,收藏这份高薪求职攻略! 文章对比了互联网大厂和国企的薪资待遇,指出虽然大厂账面薪资高,但国企在时薪、职业周期、福利等方面更具优势。文章通过三张表格详细分析了不同岗位类型、职业周期和福利待遇的差距,并建议求职者在选择工作时综合考虑各种因素,而… 2026/7/17 11:45:22
Python并发编程中的线程、进程与协程强制终止方案 1. Python并发编程中的强制终止需求在Python并发编程实践中,我们经常会遇到需要强制终止线程、协程或进程的场景。想象这样一个场景:你开发了一个Web爬虫系统,某个子线程负责下载任务,但由于目标网站响应异常,导致该线… 2026/7/17 11:41:20
算法竞赛高分突破:从数据结构到竞赛策略的232分实战指南 最近在算法圈子里,一个话题悄悄热了起来:为什么有些选手能在"镜中世界"这类周赛中稳定拿到230的高分?这背后到底有什么门道? 如果你也参加过这类算法竞赛,可能深有体会——题目看似简单,但想要拿… 2026/7/17 11:41:20
终极端到端加密云存储解决方案:ente全方位保护你的数字生活 终极端到端加密云存储解决方案:ente全方位保护你的数字生活 【免费下载链接】ente 💚 End-to-end encrypted cloud for everything. 项目地址: https://gitcode.com/GitHub_Trending/en/ente 你是否担心个人照片、敏感文件在云端被窥探࿱… 2026/7/17 11:39:19
高频PCB设计中过孔阻抗匹配与信号完整性优化 1. 高频信号传输中的过孔问题本质在PCB设计领域,过孔(Via)是连接不同层导线的关键结构,但当信号频率进入GHz范围时,这些看似简单的金属化孔洞会展现出复杂的电磁特性。我曾在多个高速数字电路项目中实测到:… 2026/7/17 11:37:18
【WPS AI表格避坑白皮书】:实测发现87%用户正在误用AI函数——这5个致命错误导致结果偏差超42% 更多请点击: https://intelliparadigm.com 第一章:WPS AI表格的核心能力与适用边界 WPS AI表格将大语言模型能力深度集成于电子表格环境中,实现从自然语言指令到结构化数据操作的端到端转化。其核心并非替代传统公式或宏编程,而是… 2026/7/17 0:00:08
Cursor终端插件生态避坑指南:23个实测低效插件黑名单,附3个自研轻量替代方案 更多请点击: https://intelliparadigm.com 第一章:Cursor终端插件生态避坑指南概览 Cursor 作为基于 VS Code 内核构建的 AI 原生编辑器,其终端插件生态虽活跃,但存在兼容性断层、权限策略突变与调试链路断裂等典型风险。开发者常… 2026/7/17 0:00:08
ChatGPT写作提示词效率革命:单条提示词响应质量提升3.8倍的关键变量(附可复用提示词矩阵表) 更多请点击: https://codechina.net 第一章:ChatGPT写作提示词效率革命:单条提示词响应质量提升3.8倍的关键变量(附可复用提示词矩阵表) 提示词工程已从经验试错迈入变量驱动的科学阶段。实证研究表明,影响… 2026/7/17 0:00:08
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/17 0:28:39
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/17 6:02:24
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/16 12:08:13