Lombok usage details


lombok can dynamically add methods to classes at compile time through annotations.

1, Configuring lombok in IDEA

1. Import lombok dependent packages.

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>

If you are using a project built with gradle, you not only need to import the lombok dependent package, but also need to specify it to be executed at compile time.

compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'

2. Install and enable the lombok plug-in

3. Enable Annotation processing

File -- > settings -- > build, execution, deployment -- > compiler -- > annotation processors -- > enable annotation processing (as shown in the figure)

2, lombok common annotations

No.annotationdescribe
01@SetterProvide setter setting methods for the properties of the class
02@GetterProvide getter acquisition methods for the properties of the class
03@ToStringProvide toString() method
04@EqualsAndHashCodeProvide the equals() and hashCode() methods
05@NoArgsConstructorNonparametric structure
06@AllArgsConstructorFully parametric structure
07@RequiredArgsConstructorSpecify parameter construction
08@CleanupAnnotations need to be placed on the stream declaration to close IO stream resources
09@DataEquivalent to @ ToString, @ EqualsAndHashCode, @ Getter and @ Setter, @ RequiredArgsConstructor of all non final fields
10@BuilderBuilder pattern
11@NonNullAvoid exception generation of NullPointException
12@Valuefinal class for annotation
13@SneakyThrowsexception handling
14@SynchronizedSynchronous method security conversion
15@LogVarious logger objects are supported, and corresponding annotations are used, such as @ Log4J, @ Slf4j

1. @ Data annotation

Create a new Message class and define three properties.

import lombok.Data;

import java.util.Date;

@Data
public class Message {
    private Integer id;
    private String content;
    private Date pubDate;
}

View the generated. class file through the decompile tool:

public class Message {
    private Integer id;
    private String content;
    private Date pubDate;

    public Message() {
    }

    public Integer getId() {
        return this.id;
    }

    public String getContent() {
        return this.content;
    }

    public Date getPubDate() {
        return this.pubDate;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public void setPubDate(Date pubDate) {
        this.pubDate = pubDate;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Message)) {
            return false;
        } else {
            Message other = (Message)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label47: {
                    Object this$id = this.getId();
                    Object other$id = other.getId();
                    if (this$id == null) {
                        if (other$id == null) {
                            break label47;
                        }
                    } else if (this$id.equals(other$id)) {
                        break label47;
                    }

                    return false;
                }

                Object this$content = this.getContent();
                Object other$content = other.getContent();
                if (this$content == null) {
                    if (other$content != null) {
                        return false;
                    }
                } else if (!this$content.equals(other$content)) {
                    return false;
                }

                Object this$pubDate = this.getPubDate();
                Object other$pubDate = other.getPubDate();
                if (this$pubDate == null) {
                    if (other$pubDate != null) {
                        return false;
                    }
                } else if (!this$pubDate.equals(other$pubDate)) {
                    return false;
                }

                return true;
            }
        }
    }

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

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        Object $content = this.getContent();
        result = result * 59 + ($content == null ? 43 : $content.hashCode());
        Object $pubDate = this.getPubDate();
        result = result * 59 + ($pubDate == null ? 43 : $pubDate.hashCode());
        return result;
    }

    public String toString() {
        Integer var10000 = this.getId();
        return "Message(id=" + var10000 + ", content=" + this.getContent() + ", pubDate=" + this.getPubDate() + ")";
    }
}

It is found from the above code that as long as the @ Data annotation is declared on the class, the corresponding setter, getter, toString(), equals(), hashCode() methods can be generated according to the properties defined by the current class. It is worth noting that the @ Data annotation does not generate a construction method.

2. @ NonNull annotation

@NonNull is used on a property. The annotated property cannot be empty.

import lombok.Data;
import lombok.NonNull;

import java.util.Date;

@Data
public class Message {
    @NonNull
    private Integer id;
    @NonNull
    private String content;
    private Date pubDate;
}

View the generated. class file through the decompile tool:

public class Message {
    @NonNull
    private Integer id;
    @NonNull
    private String content;
    private Date pubDate;

    public Message(@NonNull Integer id, @NonNull String content) {
        if (id == null) {
            throw new NullPointerException("id is marked non-null but is null");
        } else if (content == null) {
            throw new NullPointerException("content is marked non-null but is null");
        } else {
            this.id = id;
            this.content = content;
        }
    }

    @NonNull
    public Integer getId() {
        return this.id;
    }

    @NonNull
    public String getContent() {
        return this.content;
    }

    public Date getPubDate() {
        return this.pubDate;
    }

    public void setId(@NonNull Integer id) {
        if (id == null) {
            throw new NullPointerException("id is marked non-null but is null");
        } else {
            this.id = id;
        }
    }

    public void setContent(@NonNull String content) {
        if (content == null) {
            throw new NullPointerException("content is marked non-null but is null");
        } else {
            this.content = content;
        }
    }

    public void setPubDate(Date pubDate) {
        this.pubDate = pubDate;
    }

	// equals, canEqual, hashCode, toString omit pasting
}

At this time, it is found that a parameterized construction is generated after decompilation. The parameters of the construction method are the attributes marked by the @ NonNull annotation. null verification is performed on the attributes in both the construction method and the set method of the attributes marked by the @ NonNull annotation. Since there are already construction methods in the class, parameterless construction will not be generated automatically. If you want to generate parameterless construction, you need to use @ NoArgsConstructor annotation on the class, but using parameterless construction to generate objects violates the principle that @ NonNull annotation property is not empty.

3. @ RequiredArgsConstructor annotation

Annotate the @ RequiredArgsConstructor annotation on the class

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "setNonNullField")
public class Message {
    @NonNull
    private Integer id;
    @NonNull
    private String content;
    private Date pubDate;
}

View the generated. class file through the decompile tool:

private Message(@NonNull Integer id, @NonNull String content) {
    if (id == null) {
        throw new NullPointerException("id is marked non-null but is null");
    } else if (content == null) {
        throw new NullPointerException("content is marked non-null but is null");
    } else {
        this.id = id;
        this.content = content;
    }
}

public static Message setNonNullField(@NonNull Integer id, @NonNull String content) {
    return new Message(id, content);
}

@The RequiredArgsConstructor annotation can generate the private construct of the property marked by the @ NonNull annotation, and specifies the static method to call the private construct to build the object.

4. @ Builder annotation

@Builder annotations can generate builder pattern code.

import lombok.*;

import java.util.Date;

@Data
@Builder
public class Message {
    private Integer id;
    private String content;
    private Date pubDate;
}

View the generated. class file through the decompile tool:

import java.util.Date;

public class Message {
    private Integer id;
    private String content;
    private Date pubDate;

    Message(Integer id, String content, Date pubDate) {
        this.id = id;
        this.content = content;
        this.pubDate = pubDate;
    }

    public static Message.MessageBuilder builder() {
        return new Message.MessageBuilder();
    }

	// Other methods omit
	public static class MessageBuilder {
        private Integer id;
        private String content;
        private Date pubDate;

        MessageBuilder() {
        }

        public Message.MessageBuilder id(final Integer id) {
            this.id = id;
            return this;
        }

        public Message.MessageBuilder content(final String content) {
            this.content = content;
            return this;
        }

        public Message.MessageBuilder pubDate(final Date pubDate) {
            this.pubDate = pubDate;
            return this;
        }

        public Message build() {
            return new Message(this.id, this.content, this.pubDate);
        }

        public String toString() {
            return "Message.MessageBuilder(id=" + this.id + ", content=" + this.content + ", pubDate=" + this.pubDate + ")";
        }
    }
}

5. @ accessories annotation

@Accessors annotation can generate accessor pattern code, and provides three different schemes for accessor operation forms: fluent, chain and prefix.

5.1 fluent mode

import lombok.*;
import lombok.experimental.Accessors;

import java.util.Date;

@Data
@Accessors(fluent = true)
public class Message {
    private Integer id;
    private String content;
    private Date pubDate;
}

View the generated. class file through the decompile tool:

import java.util.Date;

public class Message {
    private Integer id;
    private String content;
    private Date pubDate;

    public Message() {
    }

    public Integer id() {
        return this.id;
    }

    public String content() {
        return this.content;
    }

    public Date pubDate() {
        return this.pubDate;
    }

    public Message id(Integer id) {
        this.id = id;
        return this;
    }

    public Message content(String content) {
        this.content = content;
        return this;
    }

    public Message pubDate(Date pubDate) {
        this.pubDate = pubDate;
        return this;
    }
    // Other methods omit
}

Through the above code, it is found that after the fluent mode is set, the method names of the attribute setting method and the attribute acquisition method become attribute names, which can be set directly in the form of code chain.

5.2 chain mode

import lombok.*;
import lombok.experimental.Accessors;

import java.util.Date;

@Data
@Accessors(chain = true)
public class Message {
    private Integer id;
    private String content;
    private Date pubDate;
}

View the generated. class file through the decompile tool:

import java.util.Date;

public class Message {
    private Integer id;
    private String content;
    private Date pubDate;

    public Message() {
    }

    public Integer getId() {
        return this.id;
    }

    public String getContent() {
        return this.content;
    }

    public Date getPubDate() {
        return this.pubDate;
    }

    public Message setId(final Integer id) {
        this.id = id;
        return this;
    }

    public Message setContent(final String content) {
        this.content = content;
        return this;
    }

    public Message setPubDate(final Date pubDate) {
        this.pubDate = pubDate;
        return this;
    }
	// Other methods omit
}

After using the chain mode, it is found that the setter and getter methods are generated, but the return value of the setter method is no longer void but this, so you can directly use the code chain method when setting properties.

5.3 prefix mode

import lombok.*;
import lombok.experimental.Accessors;

import java.util.Date;

@Data
@Accessors(prefix = "it")
public class Message {
    private Integer itId;
    private String itContent;
    private Date itPubDate;
}

View the generated. class file through the decompile tool:

import java.util.Date;

public class Message {
    private Integer itId;
    private String itContent;
    private Date itPubDate;

    public Message() {
    }

    public Integer getId() {
        return this.itId;
    }

    public String getContent() {
        return this.itContent;
    }

    public Date getPubDate() {
        return this.itPubDate;
    }

    public void setId(final Integer itId) {
        this.itId = itId;
    }

    public void setContent(final String itContent) {
        this.itContent = itContent;
    }

    public void setPubDate(final Date itPubDate) {
        this.itPubDate = itPubDate;
    }

    // Other methods omit
}

After using the chain mode, you can remove the declared attribute prefix from the setter and getter methods.

6. @ Cleanup annotation and @ SneakyThrows annotation

@Cleanup can automatically close resources. The @ sneakythlows annotation helps the program handle exceptions manually. The two are often used together.

import lombok.Cleanup;
import lombok.Data;
import lombok.NonNull;
import lombok.SneakyThrows;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

@Data
public class MessageRead { // The program generates a two parameter construct
    @NonNull
    private String filePath;  // File path
    @NonNull
    private String fileName;  // File name

    @SneakyThrows //Help users handle exceptions manually
    public String load() { // File reading
        @Cleanup InputStream input = new FileInputStream(new File(filePath, fileName));
        byte[] data = new byte[1024];
        int len = input.read(data);
        return new String(data, 0, len);
    }
}

View the generated. class file through the decompile tool:

import java.io.File;
import java.io.FileInputStream;
import java.util.Collections;
import lombok.NonNull;

public class MessageRead {
    @NonNull
    private String filePath;
    @NonNull
    private String fileName;

    public String load() {
        try {
            FileInputStream input = new FileInputStream(new File(this.filePath, this.fileName));

            String var4;
            try {
                byte[] data = new byte[1024];
                int len = input.read(data);
                var4 = new String(data, 0, len);
            } finally {
                if (Collections.singletonList(input).get(0) != null) {
                    input.close();
                }

            }

            return var4;
        } catch (Throwable var9) {
            throw var9;
        }
    }

	// Other methods omit
}

Through the above compiled. class file, it is found that the @ SneakyThrows annotation will generate a try catch code block after compilation, and the @ Cleanup annotation will generate a try finally code block and close resources in finally.

7. @ Synchronized annotation

@The synchronized annotation declares that the synchronized code block can be generated automatically on the method. The synchronized Object locked by the synchronized code block is the Object array automatically generated by lombok.

import lombok.SneakyThrows;
import lombok.Synchronized;

public class TicketShop {

    private Integer ticket = 10;

    @SneakyThrows
    @Synchronized
    public void sale() {
        while (ticket > 0) {
            if (ticket > 0) {
                System.out.println("[" + Thread.currentThread().getName() + "] surplus: " + ticket--);
            }
        }
    }
}

View the generated. class file through the decompile tool:

import java.io.PrintStream;

public class TicketShop {
    private final Object $lock = new Object[0];
    private Integer ticket = 10;

    public TicketShop() {
    }

    public void sale() {
        synchronized(this.$lock) {
            try {
                while(this.ticket > 0) {
                    if (this.ticket > 0) {
                        PrintStream var10000 = System.out;
                        String var10001 = Thread.currentThread().getName();
                        Integer var2 = this.ticket;
                        Integer var3 = this.ticket = this.ticket - 1;
                        var10000.println("[" + var10001 + "] surplus: " + var2);
                    }
                }
            } catch (Throwable var5) {
                throw var5;
            }

        }
    }
}

Although the @ synchronized annotation can automatically add synchronized code blocks, it has the potential of deadlock and is not recommended to be used in real projects.

Tags: Java Lombok

Posted on Sun, 24 Oct 2021 00:01:50 -0400 by JaclynM