Ah Chang teaches you to use common Lombok @ annotations

Posted by ScottCFR on Thu, 30 Dec 2021 22:30:40 +0100

preface

Old iron / old fellow friends are good!

What ah Chang shared today is the commonly used Lombok annotation, Official addressgithub address

Who doesn't know this little pepper when doing Java development. Lombok is a tool that can help us simplify and eliminate some necessary but cumbersome Java code through simple annotation, so as to improve the development efficiency of developers. By using the corresponding annotation, we can generate the corresponding method when compiling the source code.

IDEA installation

  • Install plug-ins

  • Introduce dependency
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

Common notes

Introduction to official notes

1,@Data

@Data is a convenient shortcut annotation that bundles the features of @ToString, @EqualsAndHashCode, @Getter / @Setter and @RequiredArgsConstructor is a convenient shortcut annotation that bundles the features of @ToString, @EqualsAndHashCode, @Getter / @Setter and @RequiredArgsConstructor together

@Data annotation on the class will automatically generate setter/getter, equals, canEqual, hashCode and toString methods for all properties of the class.

  • Attention

    • Annotated parameters such as callSuper, includeFieldNames, and exclude cannot be set with @ Data
    • All generated getters and setters are public To override the access level, annotate the field or class with explicit @ Setter and / or @ Getter annotations
    • All fields marked transient are not considered for hashCode and equals.
    • All static fields will be skipped completely (regardless of any generated methods, and setters / getters will not be made for them).
    • @Data(staticConstructor="of") class Foo<T> { private T x;} You can write: foo of(5); Instead of writing: to create a new instance, new foo < integer > (5);.
  • Using Lombok

    @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<T> {
            private final String name;
            private final T value;
        }
    }
    
  • Do not use Lombok

public class DataExample {
    private final String name;
    private int age;
    private double score;
    private String[] tags;

    public DataExample(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return this.age;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public double getScore() {
        return this.score;
    }

    public String[] getTags() {
        return this.tags;
    }

    public void setTags(String[] tags) {
        this.tags = tags;
    }

    @Override public String toString() {
        return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
    }

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

    @Override public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof DataExample)) return false;
        DataExample other = (DataExample) o;
        if (!other.canEqual((Object)this)) return false;
        if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
        if (this.getAge() != other.getAge()) return false;
        if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
        if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
        return true;
    }

    @Override public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final long temp1 = Double.doubleToLongBits(this.getScore());
        result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
        result = (result*PRIME) + this.getAge();
        result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
        result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
        return result;
    }

    public static class Exercise<T> {
        private final String name;
        private final T value;

        private Exercise(String name, T value) {
            this.name = name;
            this.value = value;
        }

        public static <T> Exercise<T> of(String name, T value) {
            return new Exercise<T>(name, value);
        }

        public String getName() {
            return this.name;
        }

        public T getValue() {
            return this.value;
        }

        @Override public String toString() {
            return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
        }

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

        @Override public boolean equals(Object o) {
            if (o == this) return true;
            if (!(o instanceof Exercise)) return false;
            Exercise<?> other = (Exercise<?>) o;
            if (!other.canEqual((Object)this)) return false;
            if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
            if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
            return true;
        }

        @Override public int hashCode() {
            final int PRIME = 59;
            int result = 1;
            result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
            result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
            return result;
        }
    }
    
}

2,@Getter/@Setter

You can annotate any field with @Getter and/or @Setter, to let lombok generate the default getter/setter automatically.

Use @ Getter and / or annotate any field @ Setter to have lombok automatically generate the default getter/setter.

  • Attention
    • The default getter only returns this field, and getFoo() names the field when foo (or isFoo) is called.
    • setFoo() if the field is called foo, returns void, and takes one parameter of the same type as the field, the default setter will be named. It just sets the field to this value.
    • You can use accesslevel. In annotation properties None to set the access level and manually disable getter/setter generation for any field.
  • Using Lombok
public class GetterSetterExample {
    @Getter @Setter 
    private int age = 10;

    @Setter(AccessLevel.PROTECTED) //Generate the set method and specify the range as protected
    private String name;

    @Override 
    public String toString() {
        return String.format("%s (age: %d)", name, age);
    }
}
  • Do not use Lombok
public class GetterSetterExample {
    private int age = 10;
    private String name;
    @Override public String toString() {
        return String.format("%s (age: %d)", name, age);
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    protected void setName(String name) {
        this.name = name;
    }
}

3,@NonNull

You can use @NonNull on a record component, or a parameter of a method or constructor. This will cause to lombok generate a null-check statement for you.

@NonNull is used on parameters that record components, methods, or constructors. lombok generates a null check statement for this.

  • Configuration item

Optionally, specify what exception will be thrown if an error occurs

lombok.nonNull.exceptionType= [ NullPointerException| IllegalArgumentException| JDK| Guava| Assertion]
#(default:) NullPointerException.
  • Using Lombok
public class NonNullExample extends Something {
    private String name;

    public NonNullExample(@NonNull Person person) {
        super("Hello");
        this.name = person.getName();
    }
}
  • Do not use Lombok
public class NonNullExample extends Something {
    private String name;
    public NonNullExample(@NonNull Person person) {
        super("Hello");
        if (person == null) {
            throw new NullPointerException("person is marked @NonNull but is null");
        }
        this.name = person.getName();
    }
}

4,@Cleanup

You can use @Cleanup to ensure a given resource is automatically cleaned up before the code execution path exits your current scope

@Cleanup automatically cleans up a given resource before the code execution path exits the current scope

  • Attention

    • @Cleanup InputStream in = new FileInputStream("some/file");
      //At the end of the range, in Close() will be called.
      
    • If the object type you want to clean up does not have a close() method, but there are other parameterless methods, you need to specify the close method in the annotation attribute, such as:

      @Cleanup("dispose")
      
  • Using Lombok

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);
        }
    }
}
  • Do not use Lombok
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();
            }
        }
    }
}

5,@EqualsAndHashCode

Any class definition may be annotated with @EqualsAndHashCode to let lombok generate implementations of the equals(Object other) and hashCode() methods

Use the annotation @ EqualsAndHashCode to have lombok generate implementations of the equals(Object other) and hashCode() methods

  • Attention

    • It is a bad idea to automatically generate equalsandhashCode methods for a class, because its parent class may also define fields that also require equals/hashCode code code, so the class will not generate methods.
    • By setting callSuper to real methods, you can include equals and hashCode methods generated in your parent class.
  • Using Lombok

@EqualsAndHashCode
public class EqualsAndHashCodeExample {
    private transient int transientVar = 10;
    private String name;
    private double score;
    //Specify not to generate the corresponding EqualsAndHashCode for this property
    @EqualsAndHashCode.Exclude private Shape shape = new Square(5, 10);
    private String[] tags;
    //Specify not to generate the corresponding EqualsAndHashCode for this property
    @EqualsAndHashCode.Exclude private int id;
    public String getName() {
        return this.name;
    }
    
    //callSuper also generates EqualsAndHashCode as the parent class
    @EqualsAndHashCode(callSuper=true)
    public static class Square extends Shape {
        private final int width, height;

        public Square(int width, int height) {
            this.width = width;
            this.height = height;
        }
    }
}
  • Do not use Lombok
public class EqualsAndHashCodeExample {
    private transient int transientVar = 10;
    private String name;
    private double score;
    private Shape shape = new Square(5, 10);
    private String[] tags;
    private int id;

    public String getName() {
        return this.name;
    }

    @Override public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof EqualsAndHashCodeExample)) return false;
        EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
        if (!other.canEqual((Object)this)) return false;
        if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
        if (Double.compare(this.score, other.score) != 0) return false;
        if (!Arrays.deepEquals(this.tags, other.tags)) return false;
        return true;
    }

    @Override public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final long temp1 = Double.doubleToLongBits(this.score);
        result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
        result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
        result = (result*PRIME) + Arrays.deepHashCode(this.tags);
        return result;
    }

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

    public static class Square extends Shape {
        private final int width, height;

        public Square(int width, int height) {
            this.width = width;
            this.height = height;
        }

        @Override public boolean equals(Object o) {
            if (o == this) return true;
            if (!(o instanceof Square)) return false;
            Square other = (Square) o;
            if (!other.canEqual((Object)this)) return false;
            if (!super.equals(o)) return false;
            if (this.width != other.width) return false;
            if (this.height != other.height) return false;
            return true;
        }

        @Override public int hashCode() {
            final int PRIME = 59;
            int result = 1;
            result = (result*PRIME) + super.hashCode();
            result = (result*PRIME) + this.width;
            result = (result*PRIME) + this.height;
            return result;
        }

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

6,@ToString

Any class definition may be annotated with @ToString to let lombok generate an implementation of the toString() method.

Any class definition can use the @ ToString annotation to have lombok generate an implementation of the toString() method.

  • Attention
    • By default, all non static fields are printed, and your class name and each field are printed in order, separated by commas.
    • By setting the includeFieldNames parameter to * true, * you can add some clarity (but also a certain length) to the output of the toString() method.
    • You can use @ toString Exclude to skip some fields you want to skip.
    • Set callSuper to true to include the output of the parent class's toString in the output.
    • You can use @ toString Include (rank = - 1) to change the printing order of members.
      • The default value of rank is 0
      • Higher level members print first
      • Members of the same rank are printed in the same order in which they appear in the source file.
  • Using Lombok
@ToString
public class ToStringExample {
    private static final int STATIC_VAR = 10;
    private String name;
    private Shape shape = new Square(5, 10);
    private String[] tags;
    @ToString.Exclude private int id;

    public String getName() {
        return this.name;
    }

    @ToString(callSuper=true, includeFieldNames=true)
    public static class Square extends Shape {
        private final int width, height;

        public Square(int width, int height) {
            this.width = width;
            this.height = height;
        }
    }
}
  • Do not use Lombok
public class ToStringExample {
    private static final int STATIC_VAR = 10;
    private String name;
    private Shape shape = new Square(5, 10);
    private String[] tags;
    private int id;

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

    public static class Square extends Shape {
        private final int width, height;

        public Square(int width, int height) {
            this.width = width;
            this.height = height;
        }

        @Override 
        public String toString() {
            return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
        }
    }

    @Override 
    public String toString() {
        return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
    }
}

7,@NoArgsConstructor/@RequiredArgsConstructor/@AllArgsConstructor

This set of 3 annotations generate a constructor that will accept 1 parameter for certain fields, and simply assigns this parameter to the field.

Generate a constructor that will accept 1 parameter of some fields and simply assign this parameter to the field.

  • Attention
    • @NoArgsConstructor
      • A parameterless constructor will be generated.
      • If there is only one uninitialized final attribute of this class, there will be a compilation error. You can initialize 0/ false/ null for the final attribute through @ NoArgsConstructor(force = true)
    • @RequiredArgsConstructor
      • Generate a constructor with 1 parameter.
      • All uninitialized final fields will get a parameter 0/ false/ null
    • @AllArgsConstructor
      • Generate a constructor with 1 parameter for each field in the class.
  • Using Lombok
//Specify a static constructor named "of"
@RequiredArgsConstructor(staticName = "of")
//access specifies the constructor description range
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
    private int x, y;
    @NonNull 
    private T description;

    @NoArgsConstructor
    public static class NoArgsExample {
        @NonNull 
        private String field;
    }
}
  • Lombok not used
public class ConstructorExample<T> {
    private int x, y;
    @NonNull 
    private T description;

    private ConstructorExample(T description) {
        if (description == null) throw new NullPointerException("description");
        this.description = description;
    }

    public static <T> ConstructorExample<T> of(T description) {
        return new ConstructorExample<T>(description);
    }

    @java.beans.ConstructorProperties({"x", "y", "description"})
        protected ConstructorExample(int x, int y, T description) {
        if (description == null) throw new NullPointerException("description");
        this.x = x;
        this.y = y;
        this.description = description;
    }

    public static class NoArgsExample {
        @NonNull 
        private String field;

        public NoArgsExample() {
        }
    }
}

8,@Builder

The @Builder annotation produces complex builder APIs for your classes.

Generate a statically complex builder API for the class

  • Attention

    • For the target class Foo, generate an internal class FooBuilder with the same type parameters as the static method (called builder).

      • FooBuilder includes
        • There are each attribute of the target class that is non static, and a private non static non final field.
        • A package private parameterless empty constructor.
        • toString() implementation
        • build() calls the method and passes in the method of each field. It returns a class of the same type as the one returned by the target.
        • A "setter" like method for each parameter of the target
    • @Builder. The initializer on the default field is deleted and stored in a static method to ensure that if a value is specified in the build, the initializer will not be executed at all. This does mean that the initializer cannot reference this, super, or any non static member. If lombok generates a constructor for you, it will also initialize this field with an initializer.

    • @Singular can only be used for collection types.

  • Using Lombok

@Builder
public class BuilderExample {
    @Builder.Default 
    private long created = System.currentTimeMillis();
    private String name;
    private int age;
    @Singular 
    private Set<String> occupations;
}
  • Do not use Lombok
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 + ")";
        }
    }
}
  • Use case
Person.builder().age(1).name("achang").build();

9,@Log

You put the variant of @Log on your class (whichever one applies to the logging system you use); you then have a static final log field, initialized as is the commonly prescribed way for the logging framework you use, which you can then use to write log statements.

Get an object that prints logs from a static factory

  • Attention

    • There are many log types to choose from:

      • @CommonsLog

        private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
        
      • @Flogger

        private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
        
      • @JBossLog

        private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
        
      • @Log

        private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
        
      • @Log4j

        private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
        
      • @Log4j2

        private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
        
      • @Slf4j

        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
        
      • @XSlf4j

        private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
        
      • @CustomLog

        private static final com.foo.your.Logger log = com.foo.your.LoggerFactory.createYourLogger(LogExample.class);
        
  • Using Lombok

@Log
public class LogExample {
    public static void main(String... args) {
        log.severe("Something's wrong here");
    }
}

@Slf4j
public class LogExampleOther {
    public static void main(String... args) {
        log.error("Something else is wrong here");
    }
}

@CommonsLog(topic="CounterLog")
public class LogExampleCategory {
    public static void main(String... args) {
        log.error("Calling the 'CounterLog' with a message");
    }
}
  • Do not use lombok
public class LogExample {
    private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

    public static void main(String... args) {
        log.severe("Something's wrong here");
    }
}

public class LogExampleOther {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);

    public static void main(String... args) {
        log.error("Something else is wrong here");
    }
}

public class LogExampleCategory {
    private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");

    public static void main(String... args) {
        log.error("Calling the 'CounterLog' with a message");
    }
}

10,@SneakyThrows

@SneakyThrows can be used to sneakily throw checked exceptions without actually declaring this in your method's throws clause. This somewhat contentious ability should be used carefully

@Sneakythlows can be used to automatically throw checked exceptions without actually declaring them in the throws clause of the method.

  • Using Lombok
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();
    }
}
  • Do not use 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);
        }
    }
}

11,@Accessors

The @Accessors annotation is used to configure how lombok generates and looks for getters and setters.

Chain call, how to generate and find the comments of getter and setter methods in @ Accessors Longmu island for configuration.

  • Attention

    • chain – a Boolean value. If true, the generated setter returns this instead of void.
      Default value: false, unless fluent=true, and then default value: true.
  • Use case

public class TestLombok {
    public static void main(String[] args) {
        Person achang = Person.builder().age(1).name("achang").build();
        achang.setAge(18).setName("Achang");
    }

    @Data
    @Builder
    @Accessors(chain = true)
    public static class Person{
        private Integer age;
        private String name;
    }
}

Advantages and disadvantages of Lombok

Now it's Lombok's most controversial topic, his advantages and disadvantages.

  • advantage
    • Make the code more concise and clean
    • During development, you only need to mark annotations on entity classes, which improves the efficiency of development
  • shortcoming
    • Overloading of multiple parameter constructors is not supported
    • It greatly reduces the readability and integrity of the source code and reduces the comfort of reading the source code (because many things will be omitted)
    • It will seriously affect the efficiency of packaging

ending

Lombok's biggest advantage is to make the code more concise and clean, but we can see from the above annotations that some annotations will change our coding habits. When we check the source code later, we can't view it according to the official Java syntax, but Lombok's coding.

But remember, it is just a tool. No matter how powerful this tool is, it should also be used by users. Lombok has many advantages and disadvantages that it cannot escape. What we need to do is to know its advantages and disadvantages skillfully, and the king is to use it flexibly in our actual combat.

The above is all the content shared this time. Thank you for seeing it here

Topics: Java Lombok Maven IDE intellij-idea