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文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...
随机推荐
- SpringCloud 随笔
目录 服务间通讯 统一配置中心 RabbitMQ Spring Cloud Stream 服务网关 Spring Cloud Zuul ++==(纯手打,代码可能有错!)==++ 服务间通讯 Rest ...
- Centos7.7安装expect命令工具
简单介绍 expect是一个免费的编程工具,用来实现自动的交互式任务,而无需人为干预.说白了,expect就是一套用来实现自动交互功能的软件. 在实际工作中,我们运行命令.脚本或程序时,这些命令.脚本 ...
- 自己整理的的数据操作DbHelper
using System.Data; using System.Data.SqlClient; using System.Configuration; namespace WindowsFormsAp ...
- 这些JVM命令配置参数你知道吗?
JVM是多数开发人员视为理所当然的Java功能和性能背后的重负荷机器.然而,我们很少有人能理解JVM是如何进行工作的—像任务分配和垃圾收集.转动线程.打开和关闭文件.中断和/或JIT编译Java字节码 ...
- Matlab修改数值格式/精度/小数位数
————————————命令行方法————————————— 直接在命令行中输入以下命令,但该命令不影响数据的存储形式和计算精度,下次还需进行修改. format 默认格式 format short ...
- 5. First-Class Functions
Function in python are first-class objects (runtime / element / argument / return) 1. Treating a Fun ...
- python打包工具distutils、setuptools的使用
python中安装包的方式有很多种: 源码包:python setup.py install 在线安装:pip install 包名(linux) / easy_install 包名(window) ...
- okhttp连接池:put,get方法&connection回收
OkHttp连接池put和get方法: 在上一次[https://www.cnblogs.com/webor2006/p/9281429.html]咱们分析了连接拦截器,如下: 不管是Http1.0还 ...
- HDU 4085 斯坦纳树+DP
https://cn.vjudge.net/problem/HDU-4085 给你n,m,k ,分别表示有n个点,m条边,每条边有一个权值,表示修复这条边需要的代价 从前k个点中任取一个使其和后k个点 ...
- Muduo阅读
创建了EventLoop对象的线程是IO线程,其主要功能是运行事件循环EventLoop::loop(), one loop per thread 事件循环必须在IO线程中运行 Reactor关键结构 ...