• 【转】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
    @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.里面有具体逻辑

  • 相关阅读:
    MVC4 @helper辅助方法
    Razor的主版页面框架
    Razor与HTML混合输出陷阱与技巧
    MVC4 razor与aspx的区别以及用法
    loj6100 「2017 山东二轮集训 Day1」第一题
    loj6087 毒瘤题
    「6月雅礼集训 2017 Day2」C
    「6月雅礼集训 2017 Day2」B
    「6月雅礼集训 2017 Day2」A
    「6月雅礼集训 2017 Day1」看无可看
  • 原文地址:https://www.cnblogs.com/exmyth/p/11243888.html
Copyright © 2020-2023  润新知