Lombok Pojo默认初始值问题
Lombok以注解形式来简化java代码,提高开发效率。比如我们常用的@Builder、@Data、@AllArgsConstructor、@NoArgsConstructor、@ToString等。
然最近在迭代中发现Lombok(version:1.16.20或者低于这个版本)的builder模式与new实例化或者反射机制下实例化对象默认值不兼容。这里的默认值不是基本数据类型
Lombok是通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。可以通过反编译查看生成的字节码。例子:
@Builder
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class A {
int num;
Integer count;
Integer noticedCount = 0;
}
使用方式如下
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的代码
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根据注释,这个是能解决初始化默认值的。代码如下
@Builder
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class A {
int num;
Integer count;
@Builder.Default
Integer noticedCount = 0;
}
再看看生成的A$Builder.class文件的内容如下
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的结果
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文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...
随机推荐
- Log4net采用外部配置文件和多记录器的方法
1) 创建配置文件,可以放在任意位置,名字可以任意的xml文件 例如,文件名 Log.Config.xml,内容如下 <?xml version="1.0" encoding ...
- Java实现发邮件功能---网易邮箱
目录 Java实现发邮件功能 前言 开发环境 代码 效果 结束语 Java实现发邮件功能 前言 电子邮件的应用场景非常广泛,例如新用户加入,即时发送优惠清单.通过邮件找回密码.监听后台程序,出现异常自 ...
- 14 SQLAlchemy
一. 介绍 SQLAlchemy是一个基于Python实现的ORM框架.该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL ...
- jquery.serializejson.min.js的妙用
关于这个jquery.serializejson.min.js插件来看,他是转json的一个非常简单好用的插件. 前端在处理含有大量数据提交的表单时,除了使用Form直接提交刷新页面之外,经常碰到的需 ...
- npm install 报错踩坑路
先出现的是超过最大调用栈问题: npm ERR! Maximum call stack size exceeded 百度之后说给npm降级或者升级 降级 : npm install -g npm@5. ...
- 4.安装配置bacula-gui(管理页面)
1. 安装配置bacula-gui(管理页面) Bacula-gui分为两个部分: Baculum-api可通过OAuth2(开放认证模式)或HTTP认证(用户名和密码)认证提供统一接口服务: B ...
- Kubernetes对象中的PersistentVolume、PersistentVolumeClaim和StorageClass的概念关系
Kubernetes容器要持久化数据,离不开volume,k8s的volume和Docker原生概念中的volume有一些差别,不过本次不讲这个,本次要明确的是k8s持久化数据用到的几个对象Persi ...
- 十三:MVC-HTML辅助方法-输出表单
ASP.NET MVC框架内置多个表单相关的HTML辅助方法 HTML辅助方法 说明 Html.BeginForm() 输出<form>标签 Html.CheckBox() 输出<i ...
- 一 :了解MVC
介绍 1. ASP.NET WebForm和ASP.NET MVC是并行的关系.都是属于.NET框架下的子框架. 2. MVC项目常用模板 空模板 : 不包含MVC目录结构,需要自己添加. 基本模 ...
- asterisk搭建goip
利用asterisk来搭建goip使用,步骤如下: 第一步:先需要搭建centos服务器并优化 搭建centos服务器就省略: 优化centos服务器,进行如下步骤: 关闭防火墙:systemctl ...