What is Lombok?
In their own word,
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more
Why Use Lombok
- Reduce boilerplate code
- Increase readability
- Reduce work for unit test code coverage
When to NOT use Lombok?
- More complex entity classes (non-POJO classes)
Value vs Data
Use Value
@Value
@Builder
public class AccountInfo {
private String accountId;
private String language;
private String region;
private String legalCountry;
}
For more https://projectlombok.org/features/Data
import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;
@Data public class DataExample {
private final String name;
@Setter(AccessLevel.PACKAGE) private int age;
private double score;
private String[] tags;
@ToString(includeFieldNames=true)
@Data(staticConstructor="of")
public static class Exercise {
private final String name;
private final T value;
}
}
Immutable Setters. For more https://projectlombok.org/features/With
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.With;
public class WithExample {
@With(AccessLevel.PROTECTED) @NonNull private final String name;
@With private final int age;
public WithExample(@NonNull String name, int age) {
this.name = name;
this.age = age;
}
}
To ensure the list has at least one entry
@Builder
public class MyCatalog {
@Singular("productMapById")
private Map<String, MyProduct> productById;
@Singular("validationResponse")
private List validationResponseList = new ArrayList<>();
}
Lazy Getter
Use Lombok to implement simplify Lazy Initialization
import lombok.Getter;
public class GetterLazyExample {
@Getter(lazy=true) private final double[] cached = expensive();
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}
Vanilla Java to implement Lazy Initialization
public class GetterLazyExample {
private final java.util.concurrent.AtomicReference cached = new java.util.concurrent.AtomicReference();
public double[] getCached() {
java.lang.Object value = this.cached.get();
if (value == null) {
synchronized(this.cached) {
value = this.cached.get();
if (value == null) {
final double[] actualValue = expensive();
value = actualValue == null ? this.cached : actualValue;
this.cached.set(value);
}
}
}
return (double[])(value == this.cached ? null : value);
}
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}
Clean-Up to Avoid Memory Leak
Implement Memory Leak via Lombok
import lombok.Cleanup;
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}
Vanilla Java to Implement Clean-Up
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream(args[0]);
try {
OutputStream out = new FileOutputStream(args[1]);
try {
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (in != null) {
in.close();
}
}
}
}
Implement Internal Builder
Internal Builder
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder(builderMethodName = "internalBuilder")
public class MyState {
@NotNull
private ClassA instanceA;
private List<ClassB> listInstanceB;
private List<ClassC> listInstanceC;
private List<ClassD> listInstanceD;
public static MyState builder(ClassA a) {
return internalBuilder().instanceA(a);
}
public MyState(ClassA instanceA,
List<ClassB> listInstanceB,
List<ClassC> listInstanceC,
List<ClassD> listInstanceD) {
this.instanceA = instanceA;
this.listInstanceB = listInstanceB;
this.listInstanceC = listInstanceC;
this.listInstanceD = listInstanceD;
}
}
Throw Exception, Sneakily
Sneaky Throws can be used to sneakily throw checked exceptions without actually declaring this in your method’s throws clause.
import lombok.SneakyThrows;
public class SneakyThrowsExample implements Runnable {
@SneakyThrows(UnsupportedEncodingException.class)
public String utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}
@SneakyThrows
public void run() {
throw new Throwable();
}
}
Vanilla Java
import lombok.Lombok;
public class SneakyThrowsExample implements Runnable {
public String utf8ToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw Lombok.sneakyThrow(e);
}
}
public void run() {
try {
throw new Throwable();
} catch (Throwable t) {
throw Lombok.sneakyThrow(t);
}
}
}
Synchronized
Implement Synchronization with Lombok
import lombok.Synchronized;
public class SynchronizedExample {
private final Object readLock = new Object();
@Synchronized
public static void hello() {
System.out.println("world");
}
@Synchronized
public int answerToLife() {
return 42;
}
@Synchronized("readLock")
public void foo() {
System.out.println("bar");
}
}
Implement Synchronization with Vanilla Java
public class SynchronizedExample {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public static void hello() {
synchronized($LOCK) {
System.out.println("world");
}
}
public int answerToLife() {
synchronized($lock) {
return 42;
}
}
public void foo() {
synchronized(readLock) {
System.out.println("bar");
}
}
}
Builder
With Jackson Serialization
@Value @Builder
@JsonDeserialize(builder = JacksonExample.JacksonExampleBuilder.class)
public class JacksonExample {
@Singular(nullBehavior = NullCollectionBehavior.IGNORE) private List<Foo> foos;
@JsonPOJOBuilder(withPrefix = "")
public static class JacksonExampleBuilder implements JacksonExampleBuilderMeta {
}
private interface JacksonExampleBuilderMeta {
@JsonDeserialize(contentAs = FooImpl.class) JacksonExampleBuilder foos(List<? extends Foo> foos)
}
}
@Singular
If @Singular annotation is added, then the variable name must be plural, and the singular version of the variable name would be used in the addXYZ method
@Singular List<Line> axes;
// define the singular entity format
@Singular("axis") List<Line> axes;
// To do nothing it the collection is null
@Singular(ignoreNullCollections = true
Example using Lombok
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
@Builder
public class BuilderExample {
@Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
@Singular private Set<String> occupations;
}
Using Vanilla Java
import java.util.Set;
public class BuilderExample {
private long created;
private String name;
private int age;
private Set<String> occupations;
BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
private static long $default$created() {
return System.currentTimeMillis();
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private long created;
private boolean created$set;
private String name;
private int age;
private java.util.ArrayList<String> occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder created(long created) {
this.created = created;
this.created$set = true;
return this;
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
// complicated switch statement to produce a compact properly sized immutable set omitted.
Set<String> occupations = ...;
return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
}
@java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}