Introduction to Project Lombok- Clean, Concise code

Imagine that you are coding a Java application and creating a plain old Java object (POJO), a Java class with several private fields that will require getter and setter methods to provide access. How many lines of code will be needed to generate getters and setters for each of the fields? Moreover, adding a constructor and a toString() method will cause even more lines of code and clutter. That is a lot of boilerplate code.
How about when you are utilizing Java objects that need to be closed after use, so you need to code a finally block or use try-with-resources to ensure that the object closing occurs?
Adding finally block boilerplate to close objects can add a significant amount of clutter to your code.

Project Lombok is a mature library that reduces boilerplate code. The cases mentioned above cover just a few of
those where Project Lombok can be a great benefit. The library replaces boilerplate code with easy-to-use annotations. Best of all, the library works well with popular IDEs and provides a utility to “delombok” your code by reverting—that is, adding back all the boilerplate that was removed when the annotations were added.

Download Lombok jar from:-
https://projectlombok.org/download

Now first you need to make Lombok identify your IDE, mine is MyEclipse. Double click on lombok.jar

First it will search for your IDE, if not found then you have to manually make Lombok identify your IDE by using specify location button and specifying your IDE’s home directory.

Now let’s look at major features that we get from Lombok:-

  1. Check for Nulls:- This is one of the most basic utilities that Lombok has to offer. The @NonNull annotation, which should not be confused with the Bean Validation annotation, can be used to generate a null check on a setter field. The check throws a NullPointerException if the annotated class field contains a null value. Simply apply it to a field to enforce the rule. Primitive parameters cannot be annotated with @NonNull. If
    they are, a warning is issued and no null check is generated.
  2. @NonNull @Setter
    private String employeeId;
    

  3. Concise Data Objects:- Writing a POJO can be laborious, especially if there are many fields. If you are developing a POJO, you should always provide
    private access directly to the class fields, while creating accessor methods—getters and setters—to read from and
    write to those fields. Although developing accessor methods is easy, they generally are just boilerplate code. Lombok can take care of generating these methods if a field is annotated with @Getter and @Setter. Therefore, the following two code listings provide the exact same functionality. Without Project Lombok:-
  4. private String columnName;
    public String getColumnName(){
    return this.columnName;
    }
    public void setColumnName(String columnName){
    this.columnName = columnName;
    }
    

    Using Project Lombok:-

    @Getter @Setter private String columnName;
    

    As you can see, Lombok not only makes the code more concise, but it also makes the code easier to read and less error prone. These annotations also accept an optional parameter to designate the access level if needed. More good news: @Getter and @Setter respect the proper naming conventions, so generated code for a Boolean field results
    in accessor methods beginning with is rather than get. If they are applied at the class level, getters and setters are generated for each non static field within the class. In many cases, data objects also should contain the equals(), hashCode(), and toString() methods. This boilerplate can be taken care of by annotating a class with the @EqualsAnd HashCode and @ToString annotations, respectively. These annotations cause Lombok to generate the respective methods, and they are customizable so that you can specify field exclusions and other factors. By default, any nonstatic or nontransient fields are included in the logic that is used to compose these methods. These annotations use the attribute exclude to specify methods that should not be included in the logic. The callSuper attribute accepts a true or false, and it indicates whether to use the equals() method of the super class
    to verify equality. The following code demonstrates the use of these annotations.

    @EqualsAndHashCode
    @ToString(exclude={"columnLabel"})
    public class ColumnBean {
       private BigDecimal id;
       private String columnName;
       private String columnLabel;
    }
    

    The @Data annotation can be used to apply functionality behind all the annotations discussed thus far in this section. That is, simply annotating a class with @Data causes Lombok to generate getters and setters for each of the non static class fields and a class constructor, as well as the toString(), equals(), and hashCode() methods. It also creates a constructor that accepts any final fields or those annotated with @NonNull as arguments. Finally, it generates default toString(), equals(), and hashCode() methods that take all class fields and methods into consideration. This makes the coding of a POJO very easy.

    @Data
    public class ColumnBean {
      @NonNull
      private BigDecimal id;
      @NonNull
      private String columnName;
      @NonNull
      private String columnLabel;
    }
    

    Note that if you create your own getters or setters, Lombok does not generate the code even if the annotations are present. This can be handy if you wish to develop a custom getter or setter for one or more of the class fields.
    You might be asking yourself, “Can’t my IDE already do that sort of refactoring?” Most modern IDEs—such as NetBeans, Eclipse, and IntelliJ—ofer features such as encapsulation of fields and auto-generation of code. These abilities are great because they can significantly increase productivity. However, these capabilities do not reduce code clutter, so they can lead to refactoring down the road. Let’s say your Java object has 10 fields. To conform to a JavaBean, it will contain 20 accessor methods (one getter and setter pair per field). That’s a lot of clutter. Also, what happens when you decide to change one of your field names? You’ll have to do some refactoring
    in order to change it cleanly. If you’re using Lombok, you simply change the field name and move on with your life.

  5. Builder Objects:- Sometimes it is useful to have the ability to develop a builder object, which allows objects to be constructed using a step-by-step pattern with controlled construction. For example, in some cases large objects require several fields to be populated, which can be problematic when such an object is implemented via a constructor. Lombok makes it simple to create builder objects in much the same way that it enables easy POJO creation. Annotating a class with @Builder produces a class that adheres to the builder pattern.The following could make for a sample configuration class for a REST API client:
  6. public class ApiClientConfiguration {
     
        private String host;
        private int port;
        private boolean useHttps;
     
        private long connectTimeout;
        private long readTimeout;
     
        private String username;
        private String password;
     
        // Whatever other options you may thing.
     
        // Empty constructor? All combinations?
     
        // getters... and setters?
    }
    

    We can tell the tool to generate a builder pattern, preventing us to write an extra Builder class and associated fluent setter-like methods by simply adding the @Builder annotation to our ApiClientConfiguration.

    @Builder
    public class ApiClientConfiguration {
     
        // ... everything else remains the same
     
    }
    

    Leaving the class definition above as such (no declare constructors nor setters + @Builder) we can end up using it as:

    ApiClientConfiguration config = 
        new ApiClientConfigurationBuilder()
            .host("api.server.com")
            .port(443)
            .useHttps(true)
            .connectTimeout(15_000L)
            .readTimeout(5_000L)
            .username("myusername")
            .password("secret")
        .build();
    
  7. Easy Cleanup:- Lombok makes it easy to clean up resources as well. How often have you either forgotten to close a resource or written lots of boilerplate try-catch blocks to accommodate resource closing?
    Java 7 introduced the try-with-resources block to ensure your resources held by instances of anything implementing java.lang.AutoCloseable are released when exiting. Lombok provides an alternative way of achieving this, and more flexibly via @Cleanup. Use it for any local variable whose resources you want to make sure are released. No need for them to implement any particular interface, you’ll just get its close() method called. Thanks to the @Cleanup annotation, you no longer need to worry about forgetting to release a resource.
  8. @Cleanup InputStream is = this.getClass().getResourceAsStream("res.txt");
    

    Your releasing method has a different name? No problem, just customize the annotation:-

    @Cleanup("dispose") JFrame mainFrame = new JFrame("Main Window");
    
  9. Locking Safely:- To ensure safety by having only one thread that can access a specified method at a time, the method should be marked as synchronized. Lombok supplies an even safer way to ensure that only one thread can access a method at a time: the @Synchronized annotation. The synchronized keyword to implement critical sections is not a 100% safe approach, other client code can eventually also synchronize on your instance, potentially leading to unexpected deadlocks. This is where @Synchronized comes in: annotate your methods (both instance and static) with it and you’ll get an autogenerated private, unexposed field your implementation will use for locking:
  10. @Synchronized
    public /* better than: synchronized */ void putValueInCache(String key, Object value) {
        // whatever here will be thread-safe code
    }
    
  11. Effortless Logging:- Many of us add logging statements to our code sparingly by creating an instance of a Logger from our framework of choice. Say, SLF4J:
  12. public class ApiClientConfiguration {
     
        private static Logger LOG = LoggerFactory.getLogger(ApiClientConfiguration.class);
     
        // LOG.debug(), LOG.info(), ...
     
    }
    

    This is such a common pattern that Lombok developers have cared to simplify it for us:-

    @Slf4j // or: @Log @CommonsLog @Log4j @Log4j2 @XSlf4j
    public class ApiClientConfiguration {
     
        // log.debug(), log.info(), ...
     
    }
    

    Many logging frameworks are supported and of course you can customize the instance name, topic, etc.

Leave a Reply

Your email address will not be published. Required fields are marked *