Subclasses @Builder annotation lombok the correct posture

I. Background

In the actual development, sometimes you need to use @Builder annotation subclasses lombok to use builder schema constructs the subclass object.

father:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Parent {
    private Long id;

    private String name;
}

Subclass

import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@Builder
public class Child extends Parent{


}

At this time, although the added annotation on @Builder subclasses, but since there is no attribute subclass, as shown below, can not use the builder pattern.

 

 

Second, analysis

Lombok.Builder by reading the source, not only understood @Builder annotation can be used in class, it can also be used in the constructor.

So try the following wording:

@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@Builder
public class Child extends Parent {

    @Builder
    private Child(Long id, String name) {
        super(id, name);
    }
}

Re-run unit tests above, findings support the builder mode, but the strange thing is, not through a single measure.

java.lang.AssertionError: 
Expected :1024
Actual   :null

So we look at Child.class the decompiled code:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.chujianyun.libs.lombok;

public class Child extends Parent {
    private Child(Long id, String name) {
        super(id, name);
    }

    public static Child.ChildBuilder builder() {
        return new Child.ChildBuilder();
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Child)) {
            return false;
        } else {
            Child other = (Child)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                return super.equals(o);
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof Child;
    }

    public int hashCode() {
        int result = super.hashCode();
        return result;
    }

    public String toString() {
        return "Child()";
    }

    public Child() {
    }

    public static class ChildBuilder {
        private Long id;
        private String name;

        ChildBuilder() {
        }

        public Child build() {
            return new Child();
        }

        public String toString() {
            return "Child.ChildBuilder()";
        }

        public Child.ChildBuilder id(final Long id) {
            this.id = id;
            return this;
        }

        public Child.ChildBuilder name(final String name) {
            this.name = name;
            return this;
        }
    }
}

Found the reason, while using @Builder notes in the constructor sub-class and full-parameters, there will be BUG, ​​that is, the final build () function simply returns a null parameter constructor creates a Child object, so property "adopted builder mode id and name set "final" lost. "

 

So how to solve this problem?

Once again, we return to the source of the comment @Builder:

If a member is annotated, it must be either a constructor or a method. If a class is annotated,
* then a private constructor is generated with all fields as arguments
* (as if {@code @AllArgsConstructor(access = AccessLevel.PRIVATE)} is present
* on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.

Found, it is added to the class corresponding to the private constructor contains all of the attributes, and adding annotations on @Builder constructor.

So we write the code may have a conflict, we amended as follows:

import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
public class Child extends Parent {

    @Builder
    private Child(Long id, String name) {
        super(id, name);
    }
}

The final single measured by

We look at the compiled code at this time:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.chujianyun.libs.lombok;

public class Child extends Parent {
    private Child(Long id, String name) {
        super(id, name);
    }

    public static Child.ChildBuilder builder() {
        return new Child.ChildBuilder();
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Child)) {
            return false;
        } else {
            Child other = (Child)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                return super.equals(o);
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof Child;
    }

    public int hashCode() {
        int result = super.hashCode();
        return result;
    }

    public String toString() {
        return "Child()";
    }

    public Child() {
    }

    public static class ChildBuilder {
        private Long id;
        private String name;

        ChildBuilder() {
        }

        public Child.ChildBuilder id(final Long id) {
            this.id = id;
            return this;
        }

        public Child.ChildBuilder name(final String name) {
            this.name = name;
            return this;
        }

        public Child build() {
            return new Child(this.id, this.name);
        }

        public String toString() {
            return "Child.ChildBuilder(id=" + this.id + ", name=" + this.name + ")";
        }
    }
}

At this point the build () function is the state we need.

From the compiled code we can clearly see through the core logic lombok pattern builder @Builder achieved.

That is, the internal structure of the class, the assignment properties within the class, call the constructor contains all the attributes of an object when the build is created.

 

More details can be carefully review @Builder annotated source code and comments, see the official manual  https://projectlombok.org/features/Builder

Third, the summary

Encounter strange problems must not easily let go.

Analyze problems have steps, such as whether you can have a look at the source code instructions, you can also look at the compiled code, you can also disassemble and so on, which made a comment to observe the impact of class files. You can also see the official manual.

 

 

-----------------------------------------------------

I see programmers CSDN 1024 event (October 24, 2019 deadline)

If I have a blog to help you, and there is time to welcome the browser which micro-channel scan code to help me support my point of praise:

 

 
Published 379 original articles · won praise 862 · Views 1.32 million +

Guess you like

Origin blog.csdn.net/w605283073/article/details/102470537