Lombok以注解形式来简化java代码,提高开发效率。比如我们常用的@Builder@Data@AllArgsConstructor@NoArgsConstructor@ToString等。

然最近在迭代中发现Lombok(version:1.16.20或者低于这个版本)的builder模式与new实例化或者反射机制下实例化对象默认值不兼容。这里的默认值不是基本数据类型

Lombok是通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。可以通过反编译查看生成的字节码。例子:

1
2
3
4
5
6
7
8
9
10
@Builder
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class A {
    int num;
    Integer count;
    Integer noticedCount = 0;
}

使用方式如下

1
2
3
4
5
public class Test {
    public static void main(String[] args) {
        A a = A.builder().count(1).noticedCount(2).build();
    }
}

  

这样写看着比以前的new A(),再set值方便多了,当然也可以在构造函数中直接传入需要的值。但是如果类的属性多了,就会发现Lombok使用以及开发效率上要高很多。

然而最近,在项目中使用的时候发现一个bug问题,项目中使用的Lombok的版本号1.16.20。如上面的例子,通过A.builder().build()实例化后,发现a中的noticedCount的默认值为null。究其原因,查看生成的class文件,有个A$Builder.class,使用javap -c A.class查看字节码或者直接将这个class文件拖拽到idea中,查看生成的代码,以下是在idea中展示class的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.test;
public class A$ABuilder {
    private int num;
    private Integer count;
    private Integer noticedCount;
    A$ABuilder() {
    }
    public A$ABuilder num(int num) {
        this.num = num;
        return this;
    }
    public A$ABuilder count(Integer count) {
        this.count = count;
        return this;
    }
    public A$ABuilder noticedCount(Integer noticedCount) {
        this.noticedCount = noticedCount;
        return this;
    }
    public A build() {
        return new A(this.num, this.count, this.noticedCount);
    }
    public String toString() {
        return "A.ABuilder(num=" this.num + ", count=" this.count + ", noticedCount=" this.noticedCount + ")";
    }
}

  

从中看到noticedCount默认值没有。看出A.builder().build()中的build()方法构造A对象的时候是使用内部类的属性值,所以这个初始化的实例我们的noticedCount值为空。

经过查看Lombok下的代码发现有个@Builder.Default根据注释,这个是能解决初始化默认值的。代码如下

1
2
3
4
5
6
7
8
9
10
11
@Builder
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class A {
    int num;
    Integer count;
    @Builder.Default
    Integer noticedCount = 0;
} 

再看看生成的A$Builder.class文件的内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.test;
public class A$ABuilder {
    private int num;
    private Integer count;
    private boolean noticedCount$set;
    private Integer noticedCount;
    A$ABuilder() {
    }
    public A$ABuilder num(int num) {
        this.num = num;
        return this;
    }
    public A$ABuilder count(Integer count) {
        this.count = count;
        return this;
    }
    public A$ABuilder noticedCount(Integer noticedCount) {
        this.noticedCount = noticedCount;
        this.noticedCount$set = true;
        return this;
    }
    public A build() {
        Integer noticedCount = this.noticedCount;
        if (!this.noticedCount$set) {
            noticedCount = A.access$000();
        }
        return new A(this.num, this.count, noticedCount);
    }
    public String toString() {
        return "A.ABuilder(num=" this.num + ", count=" this.count + ", noticedCount=" this.noticedCount + ")";
    }
}

可以看到代码中多了private boolean noticedCount$set;这个就是确认是否需要设置默认值。

到这一步你以为就完美了吗??NO.

假如我们在Test方法中增加一行代码,如下,自己可以试试运行的结果看看输出的a与a1的结果

1
2
3
4
5
6
7
8
public class Test {
    public static void main(String[] args) {
        A a = A.builder().count(1).noticedCount(2).build();
        System.out.println(a);
        A a1 = new A();
        System.out.println(a1);
    }
}

什么还需要new?有些场景中,比如其他第三方库使用这个类的时候,就不是通过builder模式来实例化对象,第三方库一般都是通过反射机制来实例化,然Lombok给我编译出来的class字节码已经不再是原有的。所以就出现问题了。

Lombok应该也发现了,在1.18.2以上fix这个bug了。大家可以试试。所以建议大家升级下版本

至于Lombok是如何实现的。可以研究下HandleBuilder.里面有具体逻辑

【转】Lombok Pojo默认初始值问题的更多相关文章

  1. Lombok Pojo默认初始值问题

    Lombok以注解形式来简化java代码,提高开发效率.比如我们常用的@Builder.@Data.@AllArgsConstructor.@NoArgsConstructor.@ToString等. ...

  2. C++ 变量默认初始值不确定(代码测试)

    C++ int变量默认初始值是不确定的,因此使用时初始化是很有必要的. 下面写个小程序测试一下int变量默认初始值. #include <iostream> #include <ve ...

  3. Java未赋值变量的默认初始值

    在 Java 程序中,任何变量都必须经初始化后才能被使用.当一个对象被创建时,实例变量在分配内存空间时按程序员指定的初始化值赋值,否则系统将按下列默认值进行初始化: 数据类型 初始值 byte 0 s ...

  4. C++类成员默认初始值

    有时候我们会不给C++类成员变量赋初始值,或是因为忘记在构造函数中指定(C++11可以写在类内),或是觉得没有必要写.然而,因为觉得编译器会把变量赋成0而不写是错误的.本文通过C++标准来解释这个问题 ...

  5. Java语言基础(六)char成员变量默认初始值 最简单的Java源文件 Java的main()方法

    ①char成员变量的初始值是:'\u0000' ②package用来指定该文件所处的包的名称,必须位于源文件的顶端. import java.util.*; package com.hyy.test; ...

  6. 【C++】不要依赖编译器的默认初始值

    最好在定义的时候就给出初始值. 类和结构体给出构造函数. 比如int,在vs的debug和release模式下,初始化的值是不同的.

  7. Select2实现的带搜索的省市区三级联动代码 设置默认初始值

    $(function() { $('#loc_province').select2('val','2456'); $('#loc_province').change(); $('#loc_city') ...

  8. 编写高质量代码改善C#程序的157个建议[为泛型指定初始值、使用委托声明、使用Lambda替代方法和匿名方法]

    前言 泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能.基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托 ...

  9. static 和 final 关键字 对实例变量赋初始值的影响

    static 和 final 关键字 对实例变量赋初始值的影响 最近一直在看<深入理解Java虚拟机>,在看完了对象内存分配.Class文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...

随机推荐

  1. web端自动化——Selenium Grid原理

     利用Selenium Grid可以在不同的主机上建立主节点(hub)和分支节点(node),可以使主节点上的测试用例在不同的分支节点上运行.  对不同的节点来说,可以搭建不同的测试环境(操作系统.浏 ...

  2. java当中JDBC当中请给出一个DataSource的单态模式(SingleTon)HelloWorld例子

    [学习笔记] 2.DataSource的单态模式(SingleTon)程序 咱们还接着上面的例子来说.1万个人要看书.千万确保要只建立一个图书馆.要是一不留神,建了两个或三个图书馆,那可就亏大发了.对 ...

  3. Python10之列表1(创建列表和列表添加元素的方法)

    一.创建列表 列表名 = [’元素一‘,’元素二‘,’元素三‘,’元素四‘] 列表中的元素可以是任何类型,甚至可以是一个列表. list1 = ['詹姆斯','戴维斯','保罗乔治','字母哥',in ...

  4. python学习-60 面向对象设计

    面向对象设计 1.三大编程范式 --面向过程编程 --函数式编程 --面向对象编程 2.编程进化论 --编程最开始就是无组织无结构,从简单控制流中按步写指令 --从上述的指令中提取重复的代码快或逻辑, ...

  5. SQL——INSERT INTO(增)

    一.INSERT INTO语句的基本用法 INSERT INTO 语句用于往表中插入新记录. student表: INSERT INTO语句有2种语法格式: 1.不指定列名,直接插入记录. 语法格式如 ...

  6. SPI时序

    1.串行外围接口 高速.全双工的同步通信总线 一主多从 一般速度几十MHZ,最高可以工作在上百MHZ 2.连接图  3.工作模式

  7. windows下捕获本地回环网络中的报文RawCap

    一.下载地址: 官网地址:https://www.netresec.com/?page=RawCap 百度云:链接:https://pan.baidu.com/s/1mWCOTRF5XicuJitBA ...

  8. 暑期ACM集训

    2019-07-17 08:42:11 这是总结昨天的做题情况 总体来说,好久的没做题了,实力下降了许多,这一个月假又学习吧!!!! A - Ropewalkers Polycarp decided ...

  9. Docker 方式部署的应用的版本更新

    前言 公司使用 Docker-Compose 的方式部署 Jenkins/Gitlab/Sonar/Confluence/Apollo/Harbor/ELK/MySQL 等一系列开发工具/数据库. 而 ...

  10. vue防止闪屏小技巧:[v-cloak]

    css 内添加此属性[v-cloak] { display: none; } html中引入即可 <div v-cloak> {{ message }} </div> 如果觉得 ...