【转】Lombok Pojo默认初始值问题
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@NoArgsConstructorpublic 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@NoArgsConstructorpublic 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默认初始值问题的更多相关文章
- Lombok Pojo默认初始值问题
Lombok以注解形式来简化java代码,提高开发效率.比如我们常用的@Builder.@Data.@AllArgsConstructor.@NoArgsConstructor.@ToString等. ...
- C++ 变量默认初始值不确定(代码测试)
C++ int变量默认初始值是不确定的,因此使用时初始化是很有必要的. 下面写个小程序测试一下int变量默认初始值. #include <iostream> #include <ve ...
- Java未赋值变量的默认初始值
在 Java 程序中,任何变量都必须经初始化后才能被使用.当一个对象被创建时,实例变量在分配内存空间时按程序员指定的初始化值赋值,否则系统将按下列默认值进行初始化: 数据类型 初始值 byte 0 s ...
- C++类成员默认初始值
有时候我们会不给C++类成员变量赋初始值,或是因为忘记在构造函数中指定(C++11可以写在类内),或是觉得没有必要写.然而,因为觉得编译器会把变量赋成0而不写是错误的.本文通过C++标准来解释这个问题 ...
- Java语言基础(六)char成员变量默认初始值 最简单的Java源文件 Java的main()方法
①char成员变量的初始值是:'\u0000' ②package用来指定该文件所处的包的名称,必须位于源文件的顶端. import java.util.*; package com.hyy.test; ...
- 【C++】不要依赖编译器的默认初始值
最好在定义的时候就给出初始值. 类和结构体给出构造函数. 比如int,在vs的debug和release模式下,初始化的值是不同的.
- Select2实现的带搜索的省市区三级联动代码 设置默认初始值
$(function() { $('#loc_province').select2('val','2456'); $('#loc_province').change(); $('#loc_city') ...
- 编写高质量代码改善C#程序的157个建议[为泛型指定初始值、使用委托声明、使用Lambda替代方法和匿名方法]
前言 泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能.基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托 ...
- static 和 final 关键字 对实例变量赋初始值的影响
static 和 final 关键字 对实例变量赋初始值的影响 最近一直在看<深入理解Java虚拟机>,在看完了对象内存分配.Class文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...
随机推荐
- Okhttp3基本使用
https://square.github.io/okhttp/ https://www.jianshu.com/p/da4a806e599b https://www.cnblogs.com/wzk- ...
- 【VS开发】windows下的signal
在windows下,信号机制简单来说是通过工作线程实现的,该线程运行于相对优先级THREAD_PRIORITY_HIGHEST,当信号产生时,windows生成该线程执行信号处理逻辑,由于该线程优先级 ...
- Django 之redis的应用
redis概述 redis是一种nosql数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,并且他比memcached支持更多的数据结构(string,l ...
- git push时出现大文件的处理方法
最近在提交项目时出现报错 文件限制只能100M,但是里面有个文件202M,超过了码云的限制. 所以顺手就把这个文件删除了 然后发现还是同样的报错,反复检查目录还是不行,找了资料说,需要git rm 命 ...
- [转帖]中国x86服务器市场H1出货量大幅下滑:浪潮、戴尔和华为排名前三
中国x86服务器市场H1出货量大幅下滑:浪潮.戴尔和华为排名前三 https://www.cnbeta.com/articles/tech/900237.htm 市场开始下滑了.. 据IDC<2 ...
- Mybatis应用入门
mybatis简介 Mybatis是在jdbc的基础之上封装而成的持久层框架. Mybatis是一个ORM框架.ORM(object relational mapping):对象关系型映射 搭建myb ...
- BJFU-207-基于顺序存储结构的图书信息表的逆序存储
#include<stdio.h> #include<stdlib.h> #define MAX 1000 typedef struct{ double no; char na ...
- jquery中filter()和find()函数区别
通常把这两个函数,filter()函数和find()函数称为筛选器. 下面的例子分别使用filter函数和find函数对一组列表进行筛选操作. 一组列表: <li>1</li> ...
- 【Linux】一步一步学Linux——Linux版本(03)
目录 00. 目录 01. Linux内核版本 02. Linux内核官方网站 03. Linux发行版本 04. Linux发行版本介绍 4.1 Ubuntu 4.2 RedHat 4.3 Debi ...
- Golang常用快捷键以及常见快捷键冲突
配置快捷键: 跳转到函数定义 回退 查找函数使用 File/Settings/Keymap 工具: gofmt/golint File/Settings/Tools/File Watchers gol ...