This article introduces you to the Lombok@Builder stomp hole series – constructors and default value problems. It covers basic applications, practical tips, principles and mechanisms.

Problem 1: @data and @Builder cause no-parameter constructs to be lost

The phenomenon of

  • Using the @data annotation alone produces a parameterless constructor.
  • Using the @Builder annotation alone, you find that a full-attribute constructor is generated.
  • Use @data with @Builder: we find that there is no default constructor. Adding a no-argument constructor manually or using the @noargsconstructor annotation will return an error!

Two solutions

1. The constructor adds an @tolerate annotation to let Lombok pretend it doesn’t exist (not aware)

@Builder
@Data
public class TestLombok {

    @Tolerate
    TestLombok() {
    }
    ......
} 
Copy the code

2. Add these four notes directly

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TestLombok {
    ......
}    
Copy the code

Problem 2: The @Builder annotation invalidates the defaults

The phenomenon of

Using Lombok annotations can greatly simplify code. Besides @data, there is the @Builder annotation, which makes it easy to build objects using Builder mode. However, today I found that the @Builder annotation removes the default values of objects. java

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TestLombok {

    private String aa = "zzzz";
    
    public static void main(String[] args) {
        TestLombok build = TestLombok.builder().build();
        System.out.println(build);
    }
Copy the code

Output: TestLombok app (aa = null)

The solution

Just add the @Builder.Default annotation to the field

@Builder.Default
private String aa = "zzzz";
Copy the code

The reason analysis

The way we use annotations, the underlying essence is that reflection generates a set of setters and getters for us, so we open the.class file directly under the compiled target package and see why! Source file: this

@Data @Builder @NoArgsConstructor @AllArgsConstructor public class TestLombok { private String aa = "zzzz"; public static void main(String[] args) { TestLombok build = TestLombok.builder().build(); System.out.println(build); }}Copy the code

The corresponding class bytecode: code

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.apple.ucar; public class TestLombok { private String aa = "zzzz"; public static void main(String[] args) { TestLombok build = builder().build(); System.out.println(build); } public static TestLombok.TestLombokBuilder builder() { return new TestLombok.TestLombokBuilder(); } public String getAa() { return this.aa; } public void setAa(String aa) { this.aa = aa; } public boolean equals(Object o) { if (o == this) { return true; } else if (! (o instanceof TestLombok)) { return false; } else { TestLombok other = (TestLombok)o; if (! other.canEqual(this)) { return false; } else { Object this$aa = this.getAa(); Object other$aa = other.getAa(); if (this$aa == null) { if (other$aa ! = null) { return false; } } else if (! this$aa.equals(other$aa)) { return false; } return true; } } } protected boolean canEqual(Object other) { return other instanceof TestLombok; } public int hashCode() { int PRIME = true; int result = 1; Object $aa = this.getAa(); int result = result * 59 + ($aa == null ? 43 : $aa.hashCode()); return result; } public String toString() { return "TestLombok(aa=" + this.getAa() + ")"; } public TestLombok() { } public TestLombok(String aa) { this.aa = aa; } public static class TestLombokBuilder { private String aa; TestLombokBuilder() { } public TestLombok.TestLombokBuilder aa(String aa) { this.aa = aa; return this; } public TestLombok build() { return new TestLombok(this.aa); } public String toString() { return "TestLombok.TestLombokBuilder(aa=" + this.aa + ")"; }}}Copy the code

We want to know what’s going on underneath @data, @Builder, etc., so we can compile the current file and see the code in the generated.class bytecode file

As with @Builder, there is no default value for this aa, so it will be empty!! get

  public TestLombok.TestLombokBuilder aa(String aa) {
            this.aa = aa;
            return this;
        }
Copy the code

conclusion

If you want to use @Builder, the easiest way to do this is to write these 4 annotations directly. If there is a Default value, add @Builder.Default. hash

@Data @Builder @NoArgsConstructor @AllArgsConstructor public class TestLombok { @Builder.Default private String aa = "zzzz"; public static void main(String[] args) { TestLombok build = TestLombok.builder().build(); System.out.println(build); }}Copy the code