学无先后,达者为师

网站首页 编程语言 正文

使用@Builder导致的无法创建无参构造方法

作者:搏·梦 更新时间: 2022-05-25 编程语言

文章目录

  • 1. 前言
  • 2. 先说结论
  • 3. 演示
  • 4. 解决方法
    • 1. 在实体类上再加上@AllArgsConstructor即可
    • 2. 手写个无参构造函数并加上@Tolerate
    • 3. 手写有参无参构造方法

1. 前言

  1. 在创建实体类的时候,有时候参数过于多,如果使用有参数的构造函数,实在看起来不是很优雅,一般都会想使用@Builder构建者模式来进行创建对象,不仅仅优雅,而且还方便。
  2. 当偶然机会发现,如果使用了@Builder,该实体类会失去无参构造方法
  3. 具体现象如下:
    当不加任何注解的时候,完成正常:
    在这里插入图片描述
    当加了@Builder的时候,则会直接出现下面的错误,提示没有无参的构造函数:
    在这里插入图片描述

2. 先说结论

  1. 由于没有时间深入查看@Builder的源码,但是可以从编译之后的字节码得出结论:
    1. 实体类使用了@Builder,会有全参构造函数,没有默认无参构造函数,即无法使用默认无参构造函数。
    2. 解决方法:
      1. 在实体类上同时加上:@Builder、@AllArgsConstructor、@NoArgsConstructor

      2. 手写全部有参构造方法,以及 无参构造方法

      3. 手写个默认无参构造方法,并加上@Tolerate

        @Builder
        public class demoBuilderEntry {
            private String name;
            private String sex;
        
            @Tolerate
            public demoBuilderEntry() {
            }
        }
        

3. 演示

  1. 若不知道怎么查看java反编译字节码内容,可以看一下:
    Java 如何进行反编译生成.java文件(javap、jad下载安装使用)

  2. 当实体类不加任何东西:

    public class demoBuilderEntry {
        private String name;
        private String sex;
    }
    

    可以看见是默认有无参构造函数

    public class demoBuilderEntry {
    
        public demoBuilderEntry() {
        }
    
        private String name;
        private String sex;
    }
    
  3. 当实体类加上@Builder注解:

    @Builder
    public class demoBuilderEntry {
        private String name;
        private String sex;
    }
    

    可以看出:无参构造函数已不存在,只剩全参构造函数

    public class demoBuilderEntry {
        public static class demoBuilderEntryBuilder {
    
            public demoBuilderEntryBuilder name(String name) {
                this.name = name;
                return this;
            }
    
            public demoBuilderEntryBuilder sex(String sex) {
                this.sex = sex;
                return this;
            }
    
            public demoBuilderEntry build() {
                return new demoBuilderEntry(name, sex);
            }
    
            public String toString() {
                return (new StringBuilder()).append("demoBuilderEntry.demoBuilderEntryBuilder(name=").append(name).append(", sex=").append(sex).append(")").toString();
            }
    
            private String name;
            private String sex;
    
            demoBuilderEntryBuilder() {
            }
        }
    
        demoBuilderEntry(String name, String sex) {
            this.name = name;
            this.sex = sex;
        }
    
        public static demoBuilderEntryBuilder builder() {
            return new demoBuilderEntryBuilder();
        }
    
        private String name;
        private String sex;
    }
    
    

    因此,如下图,一定会报错:
    在这里插入图片描述

    有人会说:没有无参怎么了,也没有什么事情,反正需要构造。
    但像spring这种,我们可以从配置文件中注入属性,如@ConfigurationProperties,它们的做法是先创建该类对象,使用无参构造函数,然后再调用set方法,此时,你没有默认无参构造方法,必然会失败,毕竟连对象都没有创建成功。
    具体失败案例可以看:实体类使用@Builder,导致@ConfigurationProperties注入属性失败

  4. 如果需要无参构造函数,那就手动添加,在实体类上,再加上@NoArgsConstructor也无效。
    根据下图可以看见,似乎没有报错
    在这里插入图片描述

    当运行的时候,则报错如下:与实际参数列表不符,显然是无法通过加@NoArgsConstructor创建默认无参构造函数
    在这里插入图片描述

4. 解决方法

1. 在实体类上再加上@AllArgsConstructor即可

  1. 根据下图查看,似乎也没有问题:
    在这里插入图片描述

  2. 点击运行,查看结果的时候,居然可以运行成功如下:
    在这里插入图片描述

  3. 反手我们去看一下反编译:

    
    public class demoBuilderEntry {
        public static class demoBuilderEntryBuilder {
            // builder 的代码省略,没有粘出来
        }
        
        public static void main(String args[]) {
            System.out.println(new demoBuilderEntry());
        }
    
        public static demoBuilderEntryBuilder builder() {
            return new demoBuilderEntryBuilder();
        }
    
        public demoBuilderEntry(String name, String sex) {
            this.name = name;
            this.sex = sex;
        }
    	// 无参构造函数有了
        public demoBuilderEntry() {
        }
    
        private String name;
        private String sex;
    }
    
    

2. 手写个无参构造函数并加上@Tolerate

  1. @Tolerate:中文:容许,通俗来说,被该注解加上的方法,让lombok容许它们存在。

    /**
     * Put on any method or constructor to make lombok pretend it doesn't exist,
     * i.e., to generate a method which would otherwise be skipped due to possible conflicts.
     */
    @Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Tolerate {
    }
    
  2. 测试:
    在这里插入图片描述

3. 手写有参无参构造方法

  1. 这种方法是最简单粗暴的,但是代码可能会没有那么简洁:
    在这里插入图片描述

原文链接:https://blog.csdn.net/xueyijin/article/details/124391228

栏目分类
最近更新