I’ve always believed that laziness is a virtue for programmers — only by making humans lazier do we improve efficiency, leaving repetitive and simple work to machines. Today I’m introducing a third-party library that boosts efficiency and lets you be lazy: Lombok. This time I’ll mainly show how to use Lombok to optimize your entity-class code, leaving the repetitive, simple getter/setter/toString/equals code to the program.
Lombok is a third-party Java library. In projects we often define lots of JavaBeans as entity classes, then use IDE shortcuts to generate getter/setter/toString/equals methods — the code looks huge, and worse, when you need to rename something you must do batch replacement, which can go wrong. It’s painful. Lombok can generate that complex yet low-level code at compile time. Official site: https://projectlombok.org/, GitHub: https://github.com/rzwitserloot/lombok
First, add the jar to the project. I use Maven for dependencies, so here’s the Maven pom.xml:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
Then install the plugin. To make the IDE not complain about our lazy style, install the Lombok plugin. I use IDEA, so as an example: go to File > Settings > Plugins, search for “Lombok Plugin”, click Install plugin, and restart IDEA.

Since I’ve moved to Spring Boot development, I use annotations wherever possible. With Lombok’s help, the entity class becomes:
@Data
public class Link {
private String href;
private String text;
private String target;
}
As you can see, there’s no getter/setter/toString code — just one @Data annotation, which is lombok.Data. This @Data includes @ToString, @Getter, @Setter, @EqualsAndHashCode, @NoArgsConstructor. What does each do? Let’s look at the compiled Link.class:
package com.neilren.neilren4j.entity;
public class Link {
private String href;
private String text;
private String target;
public Link() {
}
public String getHref() {
return this.href;
}
public String getText() {
return this.text;
}
public String getTarget() {
return this.target;
}
public void setHref(String href) {
this.href = href;
}
public void setText(String text) {
this.text = text;
}
public void setTarget(String target) {
this.target = target;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Link)) {
return false;
} else {
Link other = (Link)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$href = this.getHref();
Object other$href = other.getHref();
if (this$href == null) {
if (other$href == null) {
break label47;
}
} else if (this$href.equals(other$href)) {
break label47;
}
return false;
}
Object this$text = this.getText();
Object other$text = other.getText();
if (this$text == null) {
if (other$text != null) {
return false;
}
} else if (!this$text.equals(other$text)) {
return false;
}
Object this$target = this.getTarget();
Object other$target = other.getTarget();
if (this$target == null) {
if (other$target != null) {
return false;
}
} else if (!this$target.equals(other$target)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Link;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $href = this.getHref();
int result = result * 59 + ($href == null ? 43 : $href.hashCode());
Object $text = this.getText();
result = result * 59 + ($text == null ? 43 : $text.hashCode());
Object $target = this.getTarget();
result = result * 59 + ($target == null ? 43 : $target.hashCode());
return result;
}
public String toString() {
return "Link(href=" + this.getHref() + ", text=" + this.getText() + ", target=" + this.getTarget() + ")";
}
}
Now it’s clear: @Data includes @ToString, @Getter, @Setter, @EqualsAndHashCode, @NoArgsConstructor. @ToString adds a toString() method; @Getter and @Setter add get() and set() methods; @EqualsAndHashCode adds equals(Object o) and hashCode(); @NoArgsConstructor adds a no-arg constructor — here, Link(). Pretty neat, right?
A few more handy annotations:
@NonNull: adding @NonNull to a method or constructor adds a null check; on a null value it throws a NullPointerException.
@Cleanup: when using resources like InputStream/OutputStream, it auto-releases them for us — i.e., adds a close() call.
There are many more annotations I won’t cover; these common ones are basically enough. Next, a logging annotation @Slf4j. Once we bring in Lombok, @Slf4j becomes available: add @Slf4j above the class name, then use log.info() or log.error() inside the class to output and record logs:
@Slf4j
@Service
public class IpInfoService extends BaseService {
public AlicmapiIP getIpInfo(String ip) {
try {
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
}
The log output location and level can be configured via config files.
Finally, a note on Lombok: there’s been some debate on whether to use it. Use it according to your actual scenario.
