Lombok的踩坑系列之@Builder

news2024/11/28 2:38:15

背景:

Lombok 这个插件大家日常工作中几乎是必备的,几个简单的注解就可以帮助我们减少一大坨get/set方法等;其中@Builder注解使用的也很广泛,使用了建造者模式帮助我们构建出个性化的对象,本次踩坑点就在这个地方。

先讲一下踩坑的大致流程,在一个需求中需要对接口内部的一个上下文对象 增加一个属性Map,而这个上下文对象在别的接口中也有使用,那就需要兼容其他接口,所以我给这个新增的Map属性增加一个默认值 Map<Object,Object> map = Maps.newHashMap() ,然而还是获取这个属性的时候发生了异常,原因就在当前类上面的@Builder注解,下文会举一个例子具体说明一下。

举例:

package com.shizhuang.duapp.nbinterface.interfaces.facade;

import com.google.common.collect.Maps;
import lombok.Builder;
import lombok.Getter;

import java.util.Map;

@Getter
@Builder
public class CommodityModel {

    private String title;

    private Long brandId;

    private Integer channelCode;

    private Map<String,Long> extraInfoMap = Maps.newHashMap();

    public static void main(String[] args) {

        CommodityModel model = CommodityModel.builder()
                .brandId(100L)
                .title("NB 新百伦")
                .build();
        Object price = model.getExtraInfoMap().getOrDefault("price", 100L);
        System.out.println("price:" + price);
    }
}

问题来了,如上代码直接执行main方法,是否会打印出 "price: 100" ?

答案分割图

嗯哼,答案是大家贼熟悉的 NPE

看到这NPE,肯定是 extraInfoMap 这个属性是Null ,但是我们明明给了一个默认值嘛,为啥子会是Null 呢?答案就在编译后的代码中,如下:(着重关注标记的代码)

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.shizhuang.duapp.nbinterface.interfaces.facade;

import com.google.common.collect.Maps;
import java.util.Map;

public class CommodityModel {
    private String title;
    private Long brandId;
    private Integer channelCode;
    private Map<String, Long> extraInfoMap = Maps.newHashMap();

    public static void main(String[] args) {
        CommodityModel model = builder().brandId(100L).title("NB 新百伦").build();
        Object price = model.getExtraInfoMap().getOrDefault("price", 100L);
        System.out.println("price:" + price);
    }

    CommodityModel(String title, Long brandId, Integer channelCode, Map<String, Long> extraInfoMap) {
        this.title = title;
        this.brandId = brandId;
        this.channelCode = channelCode;
        this.extraInfoMap = extraInfoMap;
    }
    
    //方法1
    public static CommodityModelBuilder builder() {
        return new CommodityModelBuilder();
    }

  --- 省略Get方法 ---

    public static class CommodityModelBuilder {
        private String title;
        private Long brandId;
        private Integer channelCode;
        private Map<String, Long> extraInfoMap;

        CommodityModelBuilder() {
        }
    //方法2
        public CommodityModelBuilder title(String title) {
            this.title = title;
            return this;
        }
    //方法3
        public CommodityModelBuilder brandId(Long brandId) {
            this.brandId = brandId;
            return this;
        }

        public CommodityModelBuilder channelCode(Integer channelCode) {
            this.channelCode = channelCode;
            return this;
        }

        public CommodityModelBuilder extraInfoMap(Map<String, Long> extraInfoMap) {
            this.extraInfoMap = extraInfoMap;
            return this;
        }
    //方法4
        public CommodityModel build() {
            return new CommodityModel(this.title, this.brandId, this.channelCode, this.extraInfoMap);
        }

        public String toString() {
            return "CommodityModel.CommodityModelBuilder(title=" + this.title + ", brandId=" + this.brandId + ", channelCode=" + this.channelCode + ", extraInfoMap=" + this.extraInfoMap + ")";
        }
    }
}

Lombok的@Builder 注解在编译期间会帮我们生成一个内部的Builder类,并生成一个创建这个内部builder对象的静态方法(方法1),然后我们的代码是调用了方法1,方法2,方法3和方法4,其中方法4中的this.extraInfoMap 是内部类中的属性并没有默认值,所以build()方法返回的对象extraInfoMap就是一个null;

解决:

在需要默认值的属性上面增加 @Builder.Default 注解

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.shizhuang.duapp.nbinterface.interfaces.facade;

import com.google.common.collect.Maps;
import java.util.Map;

public class CommodityModel {
    private String title;
    private Long brandId;
    private Integer channelCode;
    private Map<String, Long> extraInfoMap;
  
    // 方法1
    private static Map<String, Long> $default$extraInfoMap() {
        return Maps.newHashMap();
    }
     --- 省略部分代码 ---

    public Map<String, Long> getExtraInfoMap() {
        return this.extraInfoMap;
    }

    public static class CommodityModelBuilder {
        private String title;
        private Long brandId;
        private Integer channelCode;
        private boolean extraInfoMap$set;
        private Map<String, Long> extraInfoMap$value;

        CommodityModelBuilder() {
        }

        public CommodityModelBuilder title(String title) {
            this.title = title;
            return this;
        }
        
        --- 省略部分代码 ---

        public CommodityModelBuilder extraInfoMap(Map<String, Long> extraInfoMap) {
            this.extraInfoMap$value = extraInfoMap;
            // 标记用户已对目标属性赋值处理了
            this.extraInfoMap$set = true;
            return this;
        }

        public CommodityModel build() {
            // this.extraInfoMap$value 是内部类的属性
            Map<String, Long> extraInfoMap$value = this.extraInfoMap$value;
            // 用户如果没有操作,则使用方法1为内部类赋值
            if (!this.extraInfoMap$set) {
                extraInfoMap$value = CommodityModel.$default$extraInfoMap();
            }
            // 使用内部类的属性创建对象
            return new CommodityModel(this.title, this.brandId, this.channelCode, extraInfoMap$value);
        }
    }
}

此时再看编译后的代码,会发现内部类中有一个属性extraInfoMap$set 会标记用户是否对extraInfoMap属性处理过,没有操作的话就会赋值我们加的默认值 = Maps.newHashMap();

总结:

日常我们业务开发中有很多小的需求,只需要增加一个属性就可以解决,此时就要注意历史逻辑中是否用 Lombok 的 Builder方式创建对象,

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

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

相关文章

智能优化算法应用:基于混沌博弈算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于混沌博弈算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于混沌博弈算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.混沌博弈算法4.实验参数设定5.算法结果6.参考…

Learning Memory-guided Normality for Anomaly Detection 论文阅读

Learning Memory-guided Normality for Anomaly Detection 摘要1.介绍2.相关工作3.方法3.1网络架构3.1.1 Encoder and decoder3.1.2 Memory 3.2. Training loss3.3. Abnormality score 4.实验5.总结总结&代码复现&#xff1a; 文章信息&#xff1a; 发表于&#xff1a;cvpr…

我最喜欢的白版应用,AI加持的新功能开源!强烈推荐

Excalidraw 把他们的文本到图表的功能开源了 Excalidraw是一个虚拟白板应用&#xff0c;专门用于绘制类似手绘的图表。它提供了一个无限的、基于画布的白板&#xff0c;具有手绘风格&#xff0c;支持多种功能。 之前我分享的&#xff1a;72张PNG&#xff0c;图解机器学习 里面…

Linux的IO模型——非阻塞IO

非阻塞IO就是当用户recvfrom时&#xff0c;如果内核数据没有准备好&#xff0c;那么就直接返回结果&#xff0c;而不是阻塞用户进程&#xff0c;让其一直等待。 我们可以发现&#xff0c;非阻塞IO模型中&#xff0c;用户进程在第一个阶段是非阻塞&#xff0c;第二个阶段是阻塞状…

Oracle(2-11)RMAN Backups

文章目录 一、基础知识1、RMAN Backup Concepts RMAN备份概念2、RMAN Backup Modes RMAN备份的类型3、Backup File Types 备份文件类型4、RMAN Backup Destinations RMAN备份目标5、Backup Constraints 备份约束6、Recovery Manager Backups 恢复管理器备份7、Characteristics …

黑苹果配置清单

手里的MacBookPro已经快沦为电子垃圾了&#xff0c;平时用MacOS比较多&#xff0c;Window用的比较少&#xff0c;而苹果电脑的价格不管是MacBookPro还是MacMini丐版的便宜但是面对现在Window动不动就64g内存的情况就显得微不足道了&#xff0c;高配的价格直接把我劝退&#xff…

iphone/安卓手机如何使用burp抓包

iphone 1. 电脑 ipconfig /all 获取电脑网卡ip&#xff1a; 192.168.31.10 2. 电脑burp上面打开设置&#xff0c;proxy&#xff0c;增加一条 192.168.31.10:8080 3. 4. 手机进入设置 -> Wi-Fi -> 找到HTTP代理选项&#xff0c;选择手动&#xff0c;192.168.31.10:8080 …

[Redis]基础入门

Redis入门 一、初识Redis Redis是一种键值型的NoSql数据库。 其中键值型&#xff0c;是指Redis中存储的数据都是以key、value对的形式存储&#xff0c;而value的形式多种多样&#xff0c;可以是字符串、数值&#xff0c;甚至是json。 NoSql则是相对于传统关系型数据库而言&a…

pthread学习遇到的问题

1.pthread_t 是个类型&#xff0c;指的是线程ID。pthread_create&#xff08;&#xff09;的时候穿地址进去&#xff0c;线程创建好后就会成为线程ID&#xff08;即输出型参数&#xff09; 2.pthread_self() pthread_self()获得是调用这个函数的线程ID &#xff08;我以为是…

Java异步编程之利器:Guava异步编程实践

第1章&#xff1a;引言 - 为什么要用Guava进行异步编程&#xff1f; 大家好&#xff0c;我是小黑&#xff01;今天咱们要聊的是Guava在异步编程中的应用。首先&#xff0c;让我们搞清楚为什么要用Guava来处理异步任务。在Java的世界里&#xff0c;异步编程是个老话题了&#x…

信号是怎么搞到电磁波上面去的呢?

在之前的文章中&#xff0c;我们曾多次讲到电磁波的美妙&#xff0c;但是有了电磁波就可以通信了吗&#xff1f; No&#xff0c;我们要把信息加载到电磁波上&#xff0c;这个电磁波就可以作为信息的载体来工作了。可是信号是怎么加载到电磁波上的呢&#xff1f; 今天我们一起…

宝塔上安装mysql遇到的问题

宝塔上安装mysql遇到的问题 文章目录 宝塔上安装mysql遇到的问题一、下载mysql二、启动报错三、设置密码四、解决报错bash未找到命令mysql五、继续修改root密码五、宝塔中设置端口六、使用连接工具连接数据库 一、下载mysql 宝塔软件商店里下载mysql&#xff0c;然后点击启动。…

SpringSecurity6 | 默认用户生成

SpringSecurity6 | 默认用户生成 ✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java…

(四) python门面模式

文章目录 4.1 结构型设计模式4.1.1 简介4.1.2 常见的几种结构型设计模式 4.2 理解门面设计模式4.2.1 门面设计模式概述4.2.2 门面设计模式的作用 4.3 UML类图4.3.1 门面4.3.2 系统4.3.3 客户端 4.4 门面模式的代码实现4.4.1 场景&#xff1a;4.4.2 python实现 4.5 原理&#xf…

【C++】动态内存管理——new和delete

这篇文章我们讲一下C的动态内存管理&#xff0c;从一个比较陌生的知识说起&#xff0c;我们知道&#xff0c;一个工程可以创建很多.c文件&#xff0c;我们如果定义一个全局变量&#xff0c;只要用extern声明一下&#xff0c;在每个文件都可以用。而用static修饰的全局变量只能在…

Vue Computed

小满&#xff0c;我的神&#xff01; 视频链接 // 只读 const plusOne computed(() > count.value 1) // 可读可写 const plusOne computed({get: () > count.value 1,set: (val) > {count.value val - 1} }, { // 用于调试onTrack(e) {debugger},onTrigger(e) …

软件测试基础知识总结

之前有将基础的软件测试知识做了一个总结&#xff0c;但比较潦草&#xff0c;很多内容只是一笔带过&#xff0c;快到年底了&#xff0c;自己也有个写年终知识总结文档的计划&#xff0c;就将基础的理论知识重新整理一番。 有人问我&#xff0c;这些都是能搜索到的知识&#xf…

微信视频无法播放,快速进行格式转换方法

你是否遇到过这样的事情呢&#xff0c;朋友或者家人在电脑上用微信给你发的视频&#xff0c;在自己的微信上点开却无法播放。这种是什么原因造成的呢&#xff1f;是不是需要将这些无法播放的视频转换为微信支持的格式才行&#xff0c;那应该如何转换呢&#xff1f; 不要着急&a…

使用 PyWebCopy 在 Python 中克隆网页

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com PyWebCopy 是一个用于克隆网页内容的 Python 库&#xff0c;它允许用户从指定的 URL 复制整个网页并保存到本地。本文将介绍 PyWebCopy 的基本用法&#xff0c;以及如何克隆网页并保存网页内容到本地文件夹。 安…

QTableView用代码设置选中状态

背景&#xff1a; 个人笔记&#xff0c;欢迎探讨。 目的是实现用代码设置表格中的选中状态&#xff0c;比如选中某个单元格&#xff0c;或某行&#xff0c;某列。实际上只要能完成选中单元格&#xff0c;行和列都是单元格的集合&#xff0c;道理一样。 QTableWidget比QTable…