A new declarative way to construct objects in Java
Tags: java, declarative, designpattern, functional
Published: 2021-11-17 04:54:59

**Declarative programming** brings many huge benefits: ease in maintainability, scalability, readability. Java is designed heavily in OOP, an imperative paradigm, which seems impossible to be declarative. However, I will show how to achieve declarative programming in Java in this article, starting with objects creation.

## Introduction

Initializing objects in Java seems to be a trivia topic. Actually, there are more things we can talk about that. 

C# - the longtime rival of Java - allows something like this ([source](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers)).

```C#
class Cat
{
    public int Age { get; set; }
    public string Name { get; set; }
}
...
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
```

This syntax looks just like declaring a JSON object. What I want to mention here is that the above code is really declarative (the field names are specified along with the values) and flexible (we can arbitrarily omit any fields).

Meanwhile, Java makes it more strict. In this article, I will introduce a solution using **chaining methods** and **lambda expression** that can unlock the same ability for Java.

## Existing approaches

### 1. Using Constructor

The traditional way to create new objects is using constructor. This may end up with too many constructors with unclear context in our codebase.

```java
public Book(String isbn, String title) {
    this.isbn = isbn;
    this.title = title;
}

public Book(String isbn, String title, String author) {
    this.isbn = isbn;
    this.title = title;
    this.author = author;
}

public Book(String isbn, String title, String author, String subCategory, Category category) {
    this.isbn = isbn;
    this.title = title;
    this.author = author;
    this.subCategory = subCategory;
    this.category = category;
}
```
To solve this problem, we can replace with static creation methods.

```java
public static Book init(String isbn, String title) {
    Book book = new Book();
    book.isbn = isbn;
    book.title = title;
    return book;
}

public static Book fullInit(String isbn, String title, String author, String subCategory, Category category) {
    Book book = new Book();
    book.isbn = isbn;
    book.title = title;
    book.author = author;
    book.subCategory = subCategory;
    book.category = category;
    return book;
}
```

### 2. Using Lombok @Builder

Eventually, constructors and static methods still lack the flexibility in *customizing the number of arguments*. To solve this problem, we can use the annotation [@Builder](https://projectlombok.org/features/Builder) of Lombok. This will allow us to omit any arguments as we want when initializing an object.

```java
Book book = Book.builder()
    .title("Sapiens: A Brief History of Humankind")
    .author("Yuval Noah Harari")
    .subCategory("History")
    .build();
```
You may wonder "Why makes it complicated? An empty constructor with some setters would just be fine". Let's think about situations that we need all in one row as below.

```java
List<Book> nonFictions = Arrays.asList(
    Book.builder().title("Sapiens: A Brief History of Humankind").author("Yuval Noah Harari").build(),
    Book.builder().title("The Defining Decade").author("Meg Jay").build(),
    Book.builder().title("The State of Affairs").author("Esther Perel").build()
);
```

### 3. Generic Typed Parameter 

Still, there is one more issue with annotation @Builder, that is the case the object has generic type parameters.

Let's have a look at this example. We want to export a list of books in table format.

![Book Table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l1yhmcissw5mdbha77bf.png)

We define a class `Column<T>` to map corresponding fields in the object.

```java
public class Column<T> {
    String title; // required
    String fieldName; // optional
    Function<T, ?> customExtractor; // optional
}
```

This will result in a list of `Column` that define the table we want to export.

```java
Table<Book> = [
  Column("Book ID", "isbn"),
  Column("Name", "title"),
  Column("Category", book -> book.category.name)
]
```

The above pseudo-code describes a table of `Book`. The first column is called "Book ID" with values taken from field `isbn`. The second column is called "Name" with values taken from `title`. The last column is "Category" with a function used to extract the target values.

Below is the corresponding Java code using Lombok @Builder.

```java
List<Column<Book>> bookTable = Arrays.asList(
    Column.<Book>builder().title("Book ID").fieldName("isbn").build(),
    Column.<Book>builder().title("Name").fieldName("title").build(),
    Column.<Book>builder().title("Category").customExtractor(book -> book.getCategory().getName()).build()
);
```

Notice that each time invoking `.builder()`, we always have to add `<Book>`, or else the compiler will fail to resolve the generic type `T`.

## Solution: Function as a Builder

Now I will introduce the solution.

We won't use the Lombok @Builder. Instead, we will modify the class `Column` to mimic its chaining behavior.

(Chaining method: a method return the object invoking it - `return this`)

```java
public class Column<T> {
    String title;
    String fieldName;
    Function<T, ?> customExtractor;

    public Column<T> title(String title) {
        this.title = title;
        return this;
    }

    public Column<T> fieldName(String fieldName) {
        this.fieldName = fieldName;
        return this;
    }

    public Column<T> customValue(Function<T, ?> customExtractor) {
        this.customExtractor = customExtractor;
        return this;
    }
}
```

To let the compiler be able to resolve type `T` at runtime, we will use a static creation method having one argument `Function<Column<T>, Column<T>>`. This is the **key concept** of the solution.

```java
public class Column<T> {
    ...

    public static <T> Column<T> add(Function<Column<T>, Column<T>> builder) {
        return builder.apply(new Column<>());
    }
}
```

(We can replace `Function<Column<T>, Column<T>>` with `UnaryOperator<Column<T>>` for the brevity)

Function `builder` will transform the empty object into a fully created object with all fields valued. Our final code will look like this.

```java
List<Column<Book>> bookTables = Arrays.asList(
    add(c -> c.title("Book ID").fieldName("isbn")),
    add(c -> c.title("Name").fieldName("title")),
    add(c -> c.title("Category").customValue(book -> book.getCategory().getName()))
);
```

The code looks much cleaner and declarative. It is more similar to our pseudo-code in the beginning.

## Conclusion

1. Applying **chaining method** (mimic the Lombok [@Builder](https://projectlombok.org/features/Builder)) allows us to customize the number of arguments without writing too many constructor.

2. Using **`Function<Entity, Entity> builder`** allows us to omit the keyword `new`

 - No more `new` in code: `new Class()`, `new Class<Entity>()`, `new Class<>()` or even `Class.<Entity>init()`. The `builder` has the ability to resolve the generic type `Entity` at runtime.

 - Thanks to *lambda expression*, we make our code much shorter with only 6 characters when creating a new object: **`o -> o...`**. Of course, we can use another variable name, but it is ok to let it be concise like that. The usage context has already been provided via the name of the invoking method (as the above example `Column.add(c -> c...)`)

## External resources

If you are interested in this pattern, here are some projects you can have a look to see how this pattern is applied.

- My Excel library: https://github.com/nambach/ExcelUtil
- SpringFu (from Spring team): https://github.com/spring-projects-experimental/spring-fu/tree/main/jafu