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 + ")"; } } }