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文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...
随机推荐
- C#动态生成Word文档并填充数据
C#也能动态生成Word文档并填充数据 http://www.cnblogs.com/qyfan82/archive/2007/09/14/893293.html 引用http://blog.csdn ...
- Js setTimeout 用法
setTimeout( ) 是属于 window 的 method, 但我们都是略去 window 这顶层物件名称, 这是用来设定一个时间, 时间到了, 就会执行一个指定的 method. setTi ...
- 一个SDL2.0程序的分析
//把图片加载到SDL_Texture SDL_Texture* loadTexture(const std::string &file, SDL_Renderer *ren){ ...
- git 报错fatal: not a git repository (or any of the parent directories): .git
产生原因:一般是没有初始化git本地版本管理仓库,所以无法执行git命令 解决方法:操作之前执行以下命令行: git init 初始化git,即可解决:
- 每日一句 Linux, 持续精进
每日一句 Linux, 持续更新 2019.12.10 1.远程登录 linux 服务器.首先要按照ssh(win10默认是安装了的).命令行窗口,使用 ssh 登录名@serverIp,之后输入密码 ...
- js重点——作用域——简单介绍(一)
一.作用域 定义:在js中,作用域为变量,对象,函数可访问的一个范围. 分类:全局作用域和局部作用域 全局作用域:全局代表了整个文档document,变量或者函数在函数外面声明,那它的就是全局变量和全 ...
- ActiveMQ入门系列三:发布/订阅模式
在上一篇<ActiveMQ入门系列二:入门代码实例(点对点模式)>中提到了ActiveMQ中的两种模式:点对点模式(PTP)和发布/订阅模式(Pub & Sub),详细介绍了点对点 ...
- docker第一篇 容器技术入门
Container 容器是一种基础工具,泛指任何可以容纳其它物品的工具. Linux Namespaces (docker容器技术主要是通过6个隔离技术来实现) namespace 系统调用参数 ...
- Xcode8警告⚠️ Empty paragraph passed to '@xxx' command
问题 Xcode8升级后,之前添加的注释会有很多警告 解决方法 基础知识,就是在编译选项中,添加警告屏蔽 解决步骤 显示警告信息 显示警告信息.png 查看警告类型 查看警告类型.png 屏蔽警告 W ...
- 8.Mapper动态代理
在前面例子中自定义 Dao 接口实现类时发现一个问题:Dao 的实现类其实并没有干什么 实质性的工作, 它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id ...