Lombok tells nothing

Factoring in Java 8, What Lombok doesn’t tell you!

Monday, December 3, 2018

Refactoring in Java 8? What the F**Bar are you talking about? Java 11 has been released, but still you are writing a blog about Java 8? YES! I do, I think there are still a lot of developers working in Java 8. The design patterns introduced in Java 8 are still relevant for the current version of Java.

Also, this blog will focus more on the design approaches and patterns available in Java rather than new language features introduced in newer versions.

Getters, Setters & Constuctors 

Raise your hand if you are using Lombok’s annotations. Indeed, almost everyone nowadays uses Lombok. Just put @Data over your constructor and you are ready to go. For the people not knowing Lombok, here is a quick example, showing the class Point, with an x and y coordinate.

public class Point {

    private Integer xCoordinate;
    private Integer yCoordinate;

    public Point() {
    }

    public Point(int xCoordinate, int yCoordinate) {
        this.xCoordinate = xCoordinate;
        this.yCoordinate = yCoordinate;
    }

    public int getxCoordinate() {
        return xCoordinate;
    }

    public void setxCoordinate(int xCoordinate) {
        this.xCoordinate = xCoordinate;
    }

    public int getyCoordinate() {
        return yCoordinate;
    }

    public void setyCoordinate(int yCoordinate) {
        this.yCoordinate = yCoordinate;
    }
}

This piece of code can be massively reduced by Lombok. Just adding @Data, @AllArgsConstructor and @NoArgsConstructor reduces this code to the following piece:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Point {

    private Integer xCoordinate;
    private Integer yCoordinate;
}

This piece of code is identically the same to the code shown in the first example. The compiler will still write getters and setters, but developers don’t have to pay attention anymore to writing boilerplate code. Absolutely genius, don’t you think?

Nullify the impact of NULL 

Now that we get the main idea of Lombok, let’s talk about why we shouldn’t use it always.
Let’s take the following example:

//What?! null? why? ¯\_(ツ)_/¯
public Integer getxCoordinate() {
    if (LocalDate.now().getDayOfWeek().equals(DayOfWeek.MONDAY)) {
        return null;
    } else {
        return xCoordinate;
    }
}

For some unknown reason, the getxCoordinate can under certain circumstances return null. Lombok will not tell you. Lombok completely hides the fact that a null can be returned, and won’t handle it. ‘Well, can I add optionals in getters? I always generate my getters and setters with my IDE ( •_•)

STOP DOING THAT! Sure you can add Optionals into your getters and setters, I encourage you to do so. If you can predict where the nulls are likely to happen, handle and tag them as quickly as you can. In order to do so, just wrap the Integer into an Optional.

public class Main {

    public static void main(String[] args) {
        List<Point> points = generatePointsList();

        List<Point> line = points.stream()
                .filter(isPointOfLine())
                .collect(Collectors.toList());
        line.forEach(System.out::println);
    }

    private static Predicate<Point> isPointOfLine() {
        return point -> point.getxCoordinate().equals(1);
    }

    private static List<Point> generatePointsList() {
        ArrayList<Point> points = new ArrayList<>();

        points.add(new Point(1, 1));
        points.add(new Point(1, 2));
        points.add(new Point(null, 3));
        points.add(new Point(2, 1));
        points.add(new Point(2, 2));
        points.add(new Point(2, 3));
        points.add(new Point(3, 1));
        points.add(new Point(3, 2));
        points.add(new Point(3, 1));
        return points;
    }
}

This simple program takes a list of Points, and returns a Line. A line (in this simple case) is formed by a set op Points where the xCoordinate equals 1.

When running the following code with the default lombok implementation, we get the famous NullPointerException.

While running the code with the Optional, and some slight modifications to the isPointOfLine method, this code manages to compile and print out a list of points which forms a line.

private static Predicate<Point> isPointOfLine() {
    return point -> point.getxCoordinate().map(x -> x.equals(1)).orElse(false);
}

Conclusion

Do we now have to write every single getter and setter ourselves? Definitely not! Lombok is made because it is very handy to use, and even when using Lombok nothing holds you back from implementing your getters and setters yourself. Simply override them when having troubles with values being null in your application.

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Point {

    private Integer xCoordinate;
    private Integer yCoordinate;

    public Optional<Integer> getxCoordinate() {
        return Optional.ofNullable(xCoordinate);
    }

    @Override
    public String toString() {
        return "Point{" +
                "xCoordinate=" + xCoordinate +
                ", yCoordinate=" + yCoordinate +
                '}';
    }
}

Don’t fix what isn’t broken, and if it is broken, wrap it into an optional. Just as simple as that.

That’s all folks!

iVonny